Meta annotation for #Controller doesn't work in Micronaut - kotlin

I am trying to implement a custom annotation (meta annotation) for #Controller as follows:
#MustBeDocumented
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.CLASS)
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
annotation class CustomController(
#get:AliasFor(annotation = Controller::class)
val value: String
)
//Usage:
#CustomController("/demo-api")
class ChangeController(private val changeGroupApi: ChangeGroupApi) {
//...
}
However, Micronaut behaves as if the controller class isn't even annotated at all. See related log, it's the same with that when the class isn't annotated.
Related log:
10:56:47.351 [default-nioEventLoopGroup-1-2] DEBUG i.m.s.rules.InterceptUrlMapRule - One or more of the IP patterns matched the host address [127.0.0.1]. Continuing request processing.
10:56:47.352 [default-nioEventLoopGroup-1-2] DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern exact match found for path [/demo-api] and method [GET]. Searching in patterns with no defined method.
10:56:47.352 [default-nioEventLoopGroup-1-2] DEBUG i.m.s.rules.InterceptUrlMapRule - No url map pattern match found for path [/demo-api]. Returning unknown.
10:56:47.352 [default-nioEventLoopGroup-1-2] DEBUG i.m.security.filters.SecurityFilter - Authorized request GET /demo-api. No rule provider authorized or rejected the request.
Any ideas to make it work?

Related

spring gateway unable to start with feign client

Using Spring 2.7.0 to create an API gateway, that was working fine until I tried to replace the RestTemplate with OpenFeign.
Here is the relevant build.gradle contents:
plugins {
id 'org.springframework.boot' version '2.7.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'org.liquibase.gradle' version '2.1.0'
id 'groovy'
}
ext {
set('springCloudVersion', "2021.0.3")
}
// implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
My application class:
#SpringBootApplication
#EnableDiscoveryClient
#EnableFeignClients
public class ApiGatewayApplication {
...
}
My FeignClient:
#FeignClient(name="identity-service")
public interface IdentityServiceClient {
#GetMapping("/api/all")
public List<ApiKey> getAllApiKeys();
}
when I try to start the application now I get:
o.s.c.openfeign.FeignClientFactoryBean : For 'identity-service' URL not provided. Will try picking an instance via load-balancing.
DiscoveryClientOptionalArgsConfiguration : Eureka HTTP Client uses RestTemplate
....
onfigReactiveWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'apiGatewayApplication': Invocation of init method failed; nested exception is feign.codec.DecodeException: No qualifying bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
com.netflix.discovery.DiscoveryClient : Shutting down DiscoveryClient ...
com.netflix.discovery.DiscoveryClient : Unregistering ...
com.netflix.discovery.DiscoveryClient : DiscoveryClient_API-GATEWAY/192.168.1.72:api-gateway:8081 - deregister status: 404
com.netflix.discovery.DiscoveryClient : Completed shut down of DiscoveryClient
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.boot.autoconfigure.http.HttpMessageConverters' in your configuration.
But none of the online examples seem to show the need for adding custom HttpMessageConverters since they are included in spring-web and spring-boot-starter-webflux
UPDATE -
this appears to be related to #Autowired. I put both my service and feign service in a regular class and it starts up fine. But when I autowire those two services, I get this error again.

Quarkus СacheManager injection in integration test

I have a Quarkus application that makes use of caches for methods.
These methods and cache eviction got to be tested somehow preferably when Quarkus context is fully operational.
This is what I figured (PostgresContainer for reference):
#QuarkusTest
class ScreeningRepositorySpec : PostgresContainer() {
private val cacheManager = CaffeineCacheSupplier().get()
init {
"test cache manager gets initialized" {
logger().info("Cache size: {}", cacheManager.size)
}
}
}
The problem arises when any kind of invocation happens for cacheManager: it fires NPE. https://github.com/im-infamou5/quarkus-cache-playground
Downstream code as follows is
#Override
public List<CaffeineCache> get() {
CacheManager cacheManager = cacheManager();
...
}
which ultimately yields:
public static CacheManager cacheManager() {
return Arc.container().instance(CacheManager.class).get();
}
And here comes that Arc.container() is null somehow.
What else was tried:
#QuarkusIntegrationTest - no bean for injection, null for arc container
Explicit #Inject for CacheManager - yields "no bean matches injection point"
Explicit Cache definition with manual CacheManager instantiation - same issue with null arc container
Variations for #Inject for default bean injection - same things about missing injection point
Looks like CacheManager bean lifecycle issue that expects it way too early and never succeeds as a result.
I've dug into quarkus tests which have quite a workaround but still hope easier approach is available to avoid this much dependensies just to test cache properly.
Version of Quarkus is 2.7.0.
Sample project can be found here with simple type of direct injection.
Output as follows:
org.acme.GreetingResourceIT > test cache size FAILED
kotlin.UninitializedPropertyAccessException at GreetingResourceIT.kt:11
Caused by: kotlin.UninitializedPropertyAccessException at GreetingResourceIT.kt:11

how can webflux handle global error, like 404 page not found

i use #restcontrolleradvice and #ExceptionHandler , but i can handle controller exception. server error like 404, 500 can't handle.
#RestControllerAdvice
public class HttpExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(HttpExceptionHandler.class);
#ExceptionHandler(value = Exception.class)
public String exceptions(Exception e) {
String code = Global.ERR_UNKNOWN;
if (e instanceof MethodNotAllowedException) {
code = Global.ERR_HTTP_METHOD;
}
return code;
}
}
If you're using Spring Boot, this is already done for you and you can customize this support as well quite easily (see Spring Boot reference docs).
If you're using plain Spring Framework, then you need to register a custom WebExceptionHandler bean to handle that (see Spring Framework reference docs). Because those errors can happen at any point during request handling (i.e. not only during the controller handling phase, but also during response encoding, within a WebFilter...), the API there is quite low level and you need to deal with raw DataBuffer instances. If you're looking for inspiration on how to achieve higher level error handling support, you can also take a look at what's done in Spring Boot.

Is it possible to possible to achieve with Jersey a polymorphic deserialisation of json payloads on POST resources

I'd like a endpoint to accept on a single verb and path a json payload that varies by a tiny bit and that can be mapped to different objects. The variation is usually on the field value like the grant_type in the OAuth world.
I scrapped stackoverflow and google on this, I found this among other
JAX-RS polymorphic POST request: how should I write my JSON?
Polymorphism in JSON, Jersey and Jackson
But those question don't seem to be relevant to me, jackson deserialisation alone works fine for this payloads, but Jersey always refuses to init the servlet with an error that let me thinks there's no way around it.
#Path("parent")
interface Resource {
#POST
#Path("test")
String test(Payload1 payload1);
#POST
#Path("test")
String test(Payload2 payload1);
#Data
#JsonTypeName("payload1")
class Payload1 extends BasePayload {
String a;
}
#Data
#JsonTypeName("payload2")
class Payload2 extends BasePayload {
String b;
}
// #JsonTypeInfo(use= Id.MINIMAL_CLASS, include=As.PROPERTY, property="#class")
#JsonTypeInfo(use= Id.NAME, include=As.PROPERTY, property="#payload")
#JsonSubTypes({
#JsonSubTypes.Type(value = Payload1.class),
#JsonSubTypes.Type(value = Payload2.class)
})
class BasePayload {
}
However I get this message in an exception upon servlet initialisation. (edited message for clarity)
</pre><p><b>root cause</b></p><pre>
org.glassfish.jersey.server.model.ModelValidationException:
Validation of the application resource model has failed during application initialization.
[[FATAL] A resource model has
ambiguous (sub-)resource method for HTTP method POST and input mime-types as defined by
"#Consumes" and "#Produces" annotations
at Java methods
public java.lang.String xxx.Service.test(xxx.Resource$Payload1)
and
public java.lang.String xxx.Service.test(xxx.Resource$Payload2)
at matching regular expression /test.
These two methods produces and consumes exactly the same mime-types and
therefore their invocation as a resource methods will always fail.;
source='org.glassfish.jersey.server.model.RuntimeResource#59b668bf']
Note however that having a endpoint with the parent class of the payload works, but you have to handle the dispatch yourself.
#POST
#Path("test")
String test(BasePayload payload);
I'm using Spring-Boot 1.4 / Jersey 2.23.2 / Jackson 2.8.5
The JAX-RS runtime uses the following for matching requests to resource methods:
URI: Defined in the #Path annotation.
Request method: Defined by a resource method designator such as #GET, #POST, etc).
Media type: Defined in Accept and Content-Type headers, that are matched with the values defined in the #Produces and #Consumes annotations, respectively.
The content of the payload is not taken into account, so it makes your first method definition ambiguous.
As you already figured out, the following approach is the way to go:
#POST
#Path("test")
String test(BasePayload payload);

RestEasy #ValidateRequest is not working

I am having below configuration for a RestEasy Rest WS
jaxrs-api-2.3.5.Final.jar,
resteasy-jaxrs-2.3.5.Final.jar,
resteasy-hibernatevalidator-provider-2.3.5.Final.jar,
hibernate-validator-4.3.2.Final.jar,
validation-api-1.0.0.GA.jar
I have added #ValidateRequest on class(tried on method as well) to validate any request input data before processing the request but i dont know why validation in not being invoked.
#Path(value = "/events")
#ValidateRequest
public class EventRestController {
#GET
#Produces({ MediaType.APPLICATION_XML, ACCEPT_HEADER })
public Response get(#QueryParam("componentSerialNumber") #NotNull String componentSerialNumber) {
System.out.println("powerChangeEvevnt.getComponentSerialNumber() " + componentSerialNumber);
return Response.ok().build();
}
}
i dont know what i am missing.
please suggest.
Turning on auto scanning of Rest resources and providers solved the issue, validation started working.
just set resteasy.scan parameter value to true in web.xml
i had explicitly registered all resources in a subclass extending javax.ws.rs.Application class but it was not considering HibernateValidator as a validator for validation. i found registering HibernateValidator as a validator quite complex, so just removed this explicitly registered configuration class and enabled Automatically scan.