Add another UrlHandlerMapping to Spring data rest - spring-data-rest

I'm having a normal spring-mvc project and I'm also building a rest module as a separate jar file. The goal is when I have the rest jar in my classpath to have the normal website mapped to / and the spring-data-rest repositories mapped to /rest. For the rest module I have defined RepositoryRestMvcConfiguration as well as a WebApplicationInitializer and it all works fine.
So now I want to add some more URLs to the rest module (like /synchronize, and /authenticate, etc.) but as soon as I add controllers in the rest module, they are picked up by the parent application context (the one for the website /). I tried specifying them as bean in the RepositoryRestMvcConfiguration but still they are picked up by the other parent context and the filters of the parent context are fired. And when I access the spring-data-rest through /rest no filters are triggered.
So I was wondering: is there a method I could override in the RepositoryRestMvcConfiguration so that I can add extra url handler mappings?

I assume you mean that you want to have another controller advertised as part of Spring Data REST's root hypermedia.
To do so, you need to create another class in your app like this:
#Component
class DogifierResourceProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Override
public RepositoryLinksResource process(RepositoryLinksResource objects) {
objects.add(new Link(ServletUriComponentsBuilder.fromCurrentRequest()
.build()
.toUriString()
.concat("dogifier/{id}"), "dogifier"));
return objects;
}
}
This will create a hypermedia entry with rel="dogifier" that lists /dogifier/{id} as the URI. It will also prefix it with the proper URN, etc.
Of course, you can use Spring HATEOAS to link to a controller method without having to specify the actual path by hand. That would reduce maintenance and encourage better hypermedia controls.

You need to exclude those controllers from the classpath scanning of the parent context. Just follow the instructions in the Spring documentation.

Related

Why ClassGraph finds classes from other deployment on Wildfly?

I prepared WAR archive and I deployed it on Wildfly 21.
I want to find all classes annotated with javax.ws.rs.ApplicationPath annotation.
try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages( PACKAGE ).scan())
{
final ClassInfoList classesWithAnnotation =
scanResult.getClassesWithAnnotation( ApplicationPath.class.getName() );
}
It finds two classes, one from my deployment and one from other deployment. I undeployed other deployment to make sure that one class is taken from it and I am sure, if only one deployment is deployed on Wildfly then it return only one class.
I do not understand why ClassGraph finds classes from other deployment. How to configure ClassGraph to scan only deployment which ClassGraph itself belongs? I guess that I have to configure class loader, but I do not know how to do it correctly.
Unfortunately it seems that there is no any configuration options that could meet my requirements. Source code modification is needed.
There is JBossClassLoaderHandler#findClasspathOrder method.
First org.jboss.modules.Module is retrieved from class loader. Next getCallerModuleLoader method is invoked and returns different class loader. Original class loader is an instance of org.jboss.modules.ModuleClassLoader class. Class loader retrieved by getCallerModuleLoader method is an instance of org.jboss.as.server.moduleservice.ServiceModuleLoader class. Next moduleMap method is invoked and returns map of modules. This map contains both my deployments. Next operations are executed for each map entry.
I also do not see possibility to add my own implementation of nonapi.io.github.classgraph.classloaderhandler.ClassLoaderHandler without source code modification because handlers are kept in unmodifiable final filed in ClassLoaderHandlerRegistry class.

access ResourceInfo in Quarkus / Vertx HttpAuthenticationMechanism

In Quarkus (resteasy reactive), is there a way to get hold of the "ResourceInfo" in an HTTP Authentication Mechanism?
What I'm trying to do is read an annotation that is defined on the resource class or method, in order to choose an authentication mechanism based on it.
Injecting the ResourceInfo directly in the mechanism class does not work (and also, it is application scoped and not request scoped, so not sure it could work). I also couldn't find the info I need in the RoutingContext parameter.
I have also tried adding a ContainerRequestFilter, in which injecting the ResourceInfo with #Context works well, but I think perhaps the filters are called after the httpAuthenticationMechanism.authenticate(), because it's not called in my test when the endpoint requires authentication.
Is there another way to do this?
----> To clarify with code what I would like to do:
have different JAX-RS resources with a custom #Authorization annotations with different "api names" like this:
#Path("/jwttest")
#ApplicationScoped
#Authorization("jwttest")
public class JWTTestController {
...
}
#Path("/oidctest")
#ApplicationScoped
#Authorization("myoidc")
public class OIDCTestController {
...
}
and then different configs like this:
myframework.auth.jwttest.type=jwt
myframework.auth.jwttest.issuer=123
myframework.auth.jwttest.audience=456
myframework.auth.myoidc.type=oidc
myframework.auth.myoidc.auth-server-url=myurl
And in the HttpAuthenticationMechanism, find the value of #Authorization, and based on it, call another provider like suggested in https://quarkus.io/guides/security-customization#dealing-with-more-than-one-httpauthenticationmechanism with the right api name so that it can load the config.

User scoped dependencies in a custom ASP.NET Core Action Filter?

According to the official documentation here:
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#authorization-filters
To implement a custom ActionFilter in ASP.NET Core I have three choices:
SeviceFilterAttribute
TypeFilterAttribute
IFilterFactory
But for all three it is stated that:
Shouldn't be used with a filter that depends on services with a lifetime other than singleton.
So how can I inject scoped services in my custom ActionFilter? I can easily get a scoped service from the current HttpContext like this:
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
ISubscriptionHelper subscriptionHelper =
actionContext.HttpContext.RequestServices
.GetRequiredService<ISubscriptionHelper>();
}
But then I am wondering if I am doing something wrong? What is the correct way to depend on scoped services in a custom ActionFilterAttribute?
Resolving services from the HttpContext.RequestServices will correctly resolve scoped and transient instances without causing any problems such as Captive Dependencies. In case resolved components implement IDisposable, they will be disposed of when the request ends. ASP.NET Core passes on the current HttpContext object to filter's OnActionExecuting method and that HttpContext gives access to the DI Container.
This is completely different from injecting those services into the constructor, because the action filter will be cached for the lifetime of the application. Any dependencies stored in private fields will, therefore, live as long as that filter. This leads to the so called Captive Dependency problem.
Code that accesses the DI Container (the HttpContext.RequestServices is your gateway into the DI Container) should be centralized in the infrastructure code of the startup path of the application—the so called Composition Root. Accessing your DI Container outside the Composition Root inevitably leads to the Service Locator anti-pattern—this should not be taken lightly.
To prevent this, it is advised to keep the amount of code inside the action filter as small as possible and implement the filter as a Humble Object. This means that preferably, the only line of code inside the filter is the following:
actionContext.HttpContext.RequestServices
.GetRequiredService<ISomeService>() // resolve service
.DoSomeOperation(); // delegate work to service
This means all (application) logic is moved to the ISomeService implementation, allowing the action filter to become a Humble Object.

How to change the operation name of a span with Apache Camel OpenTracing component?

I successfully added Apache Camel's OpenTracing component to my application. I can see traces in Jaeger UI. But the traces for the RabbitMQ component show only the exchange name without the routing key as operation name. Because of my application uses only one exchange with different routing keys, I need to see the routing key as operation name in my traces.
Research
With OpenTracing Spring RabbitMQ I could expose another customized RabbitMqSpanDecorator, see Span decorator:
Note: you can customize your spans by declaring an overridden RabbitMqSpanDecorator bean.
(However, I coulnd't change the operation name with the RabbitMqSpanDecorator at all, because the operation name is hard coded to producer or consumer.)
Unfortunately Apache Camel uses its own different implementation of a RabbitmqSpanDecorator to decorate spans. I wrote a custom class by overiding Apache Camel's RabbitmqSpanDecorator, but my custom class wasn't used.
Question
How can I change the operation name of a span with Apache Camel OpenTracing component for Apache Camel RabbitMQ component?
It is possible to change the Tracer implementation with ServiceLoader, see OpenTracing:
EXPLICIT
Include the camel-opentracing component in your POM, along with any specific dependencies associated with the chosen OpenTracing compliant Tracer.
To explicitly configure OpenTracing support, instantiate the OpenTracingTracer and initialize the camel context. You can optionally specify a Tracer, or alternatively it can be implicitly discovered using the Registry or ServiceLoader.
With the DefaultTracer it is also possible to change the RabbitmqSpanDecorator with ServiceLoader, see Tracer.java:
static {
ServiceLoader.load(SpanDecorator.class).forEach(d -> {
SpanDecorator existing = DECORATORS.get(d.getComponent());
// Add span decorator if no existing decorator for the component,
// or if derived from the existing decorator's class, allowing
// custom decorators to be added if they extend the standard
// decorators
if (existing == null || existing.getClass().isInstance(d)) {
DECORATORS.put(d.getComponent(), d);
}
});
}
Therefore, I had to add a file org.apache.camel.tracing.SpanDecorator containing the name of my custom RabbitmqSpanDecorator, see ServiceLoader:
Deploying service providers on the class path
A service provider that is packaged as a JAR file for the class path is identified by placing a provider-configuration file in the resource directory META-INF/services. The name of the provider-configuration file is the fully qualified binary name of the service. The provider-configuration file contains a list of fully qualified binary names of service providers, one per line.
My custom RabbitmqSpanDecorator:
public class CustomRabbitmqSpanDecorator extends RabbitmqSpanDecorator {
#Override
public String getOperationName(Exchange exchange, Endpoint endpoint) {
return ((RabbitMQEndpoint) endpoint).getRoutingKey();
}
}

How to write single objectMapper for spring-data-rest and to my #RestController class

I wrote a UnwrappingBeanSerializer for my entity. Currently this serializer was registered using ConfigureJacksonObjectMapper
This serializer is working fine for REST APIs generated from spring-data-rest. But I have a custom #RestController for the same entity, But it doesn't know about the serializer registered in spring-data-rest configuration.
I want to serialize my response with UnwrappingBeanSerializer both in spring-data-rest APIs and also to my custom controllers.
How to achieve this?
I also tried with #JsonSerialize on my entity class. But I am unable to create bean for unWrappingBeanSerializer with BeanSerializerBase
Regular #RestController and Spring Data REST controllers have different flows and configuration. If you are using Spring Data REST, you'd better use #RepositoryRestController for custom endpoints of the same resource, this will use the same Spring Data REST chain and its configuration, like the one you used in ConfigureJacksonObjectMapper, otherwise your ObjectMapper is visible only for Spring Data REST.
If you want to have #RestController and use the same ObjectMapper for both - you need to have two configurations: one for Spring Data REST (like you already have) and another for regular controllers, so just register it in Spring context (for instance, if you are using Spring MVC, see Customize the Jackson ObjectMapper).