REST client interface can not be injected in Quarkus kotlin app - kotlin

I tried to add a quarkus-rest-client sample for my post-service which is a simple REST API built with Quarkus.
The java version is working well.
When I added another Kotlin to test the kotlin and Gradle support in Quarkus, it failed, the REST Client interface can not be injected as CDI bean.
The PostControlloer is Jaxrs resource to expose an aggregated APIs that combined the original two APIs.
#Path("/api")
#RequestScoped
class PostController(#Inject #RestClient val client: PostResourceClient) {
// #Inject
// #RestClient
// lateinit var client: PostServiceClient
#GET
#Produces(MediaType.APPLICATION_JSON)
fun getPosts(#QueryParam("q")
q: String,
#QueryParam("offset")
#DefaultValue("0")
offset: Int,
#QueryParam("limit")
#DefaultValue("10")
limit: Int): Response {
val posts = this.client.getAllPosts(q, offset, limit).entity as List<Post>
val count = this.client.countAllPosts(q).entity as Long
return ok(PostPage(posts, count)).build()
}
}
The above two approaches to inject a Bean are failed.
The REST Client interface:
#Path("/posts")
#RegisterRestClient
interface PostResourceClient {
#GET
#Produces(MediaType.APPLICATION_JSON)
fun getAllPosts(
#QueryParam("q")
q: String,
#QueryParam("offset")
#DefaultValue("0")
offset: Int,
#QueryParam("limit")
#DefaultValue("10")
limit: Int
): Response
#GET
#Path("count")
#Produces(MediaType.APPLICATION_JSON)
fun countAllPosts(
#QueryParam("q")
q: String
): Response
}
The application config for this Rest Client interface.
com.example.PostResourceClient/mp-rest/url=http://localhost:8080
com.example.PostResourceClient/mp-rest/scope=javax.inject.Singleton
The complete codes is here.

Duplicated with Error to inject some dependency with kotlin + quarkus it is a MicroProfile RestClient issue. See the workaround in the original SO answer:
#Inject
#field: RestClient
lateinit internal var countriesService: CountriesService
An issue is already openned on MicroProfile RestClient to have a fix for this and tracked on the Quarkus issue traker: https://github.com/quarkusio/quarkus/issues/5413

Related

How to monitor meters in Spring Webflux for a reactor-netty server

I am new to Spring Boot and Spring Webflux. I am working on a Spring Webflux reactor-netty server to handle WebSocket connections. In the simplest sense, this is how the server looks like:
...
#Component
public class ServerWebSocketHandler implements WebSocketHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
public Mono<Void> handle(WebSocketSession session) {
String sessionId = session.getId();
Sinks.Many<String> unicastSink = Sinks.many().unicast().onBackpressureError();
// save the unicastSink in cache so that on demand messages can be sent to the sink
Mono<Void> receiver =
session
.receive()
.map(WebSocketMessage::getPayloadAsText)
.doOnNext(message -> this.handleIncomingMessage(sessionId, message))
.doOnError(error -> {
logger.info("Error occurred in the session - Session: '{}'; Error: '{}'", sessionId, error);
})
.doFinally(s -> {
this.cleanUp(sessionId, s);
})
.then();
Mono<Void> sender =
session
.send(unicastSink.asFlux().map(session::textMessage));
return Mono.zip(receiver, sender).then();
}
// handleIncomingMessage, cleanUp, and other private methods to handle business logic
}
Now, I want to monitor the meters, specifically meters that can help in identifying back pressure or memory leak like reactor.netty.eventloop.pending.tasks, reactor.netty.bytebuf.allocator.used.direct.memory, reactor.netty.bytebuf.allocator.used.heap.memory. I read about these meters in Reactor Netty Reference Guide https://projectreactor.io/docs/netty/1.1.0-SNAPSHOT/reference/index.html#_metrics. The example of how to enable it is done on the server creation, but in Webflux, all these are abstracted out. So, my question is, in this case, how can I enable monitoring the meters and how to consume the meter. A small example code which shows how to do it would be greatly useful.
You can use Spring Boot API for configuring the web server
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.webserver.configure
#Component
public class MyNettyWebServerCustomizer
implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
#Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(httpServer -> httpServer.metrics(...));
}
}
These built-in Reactor Netty metrics use Micrometer so you can consume them with everything that has integration with Micrometer.

How to inject HTTP client from library in Micronaut app?

I have an external http client to other service and I've imported it to my micronaut app but receives an error which points:
Caused by: io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [xxx.files.client.micronaut.FilesClient] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
I've added these classes when the app starts:
object TransformerApplication {
#JvmStatic
fun main(args: Array<String>) {
Micronaut.build()
.packages(
"xxx.transformer",
"xxx.files.client.micronaut"
)
.mainClass(TransformerApplication.javaClass)
.start()
}
}
But when creating:
#KafkaListener("transformer-group")
class EventsConsumerImpl(private val xlsTransformer: XlsTransformer,
private val filesClient: FilesClient,
private val workspacesClient: WorkspacesClient) : EventsConsumer {
...
}
My http-client:
#io.micronaut.http.client.annotation.Client("\${files.url}")
interface FilesClient {
companion object {
const val FILES_TOKEN_KEY = "FILES"
}
#FilesTokenVerification
#Get("\${files.url}/{fileId}")
fun getFile(#PathVariable fileId: String): ByteArray
#FilesTokenVerification
#Patch("\${files.url}/{fileId}/info")
fun updateFileStatus(#PathVariable fileId: String, #Body metadata: Metadata): Metadata
#FilesTokenVerification
#Get("\${files.url}/{fileId}/info")
fun getFileMetadata(#PathVariable fileId: String): Metadata
}```
Can someone explain to me what I'm doing wrong?
is usuallay use a factory for that:
https://docs.micronaut.io/latest/guide/index.html#factories
but i do not know if that works with declarative clients.
Maybe try a wrapper around.

Jackson annotations quarkus resteasy client

I have a client package where I have defined my REST clients, containing the following interface and models:
#Path("/")
#RegisterRestClient(configKey = "some-api")
public interface SomeRestClient {
#POST
#Path("/oauth/token")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
OAuthResult getOAuthToken(OAuthRequest oAuthRequest);
}
and OAuthRequest class:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class OAuthRequest {
private String email;
private String password;
#JsonProperty("grant_type")
private String grantType;
#JsonProperty("client_id")
private String clientId;
#JsonProperty("client_secret")
private String clientSecret;
}
I import this package into my main service package, but quarkus does not seem to pick up the Jackson annotations. The properties of the request are serialized using camel case, when they should be in snake case.
#Inject
#RestClient
private SomeRestClient someRestClient;
OAuthRequest oAuthRequest = new OAuthRequest();
//set fields
OAuthResult oAuthResult = someRestClient.getOAuthToken(oAuthRequest);
EDIT:
I am using the quarkus-rest-client-jackson and the quarkus-rest-client dependencies, no jsonb dependency anywhere.
I have tried to narrow the problem down: I have moved the client / request classes to my main package, and I have removed the lombok annotations and made my fields which have Jackson annotations public. Still the same problem...
Could anyone point me in the right direction of what I am doing wrong?
Reasteasy is used for your reste endpoint not to access a remote REST service.
To access a remote REST service the REST client is used.
The REST client comes with Jackson support of you use the quarkus-rest-client-jackson dependency not the JSON-B one.

CDI injection not working in REST Resource in WAS Liberty with Jersey as JAX-RS implementation

I am using websphere liberty 19.0.0.8 and I wanted to use Jersey instead of default CXF for jax-rs implementation. I removed jaxrs-2.1 feature from server xml and packaged jersey implementation jars in my webapp .war.
<featureManager>
<feature>servlet-4.0</feature>
<feature>jndi-1.0</feature>
<feature>requestTiming-1.0</feature>
<feature>monitor-1.0</feature>
<feature>localConnector-1.0</feature>
<feature>restConnector-2.0</feature>
<!-- Do not add enabled webProfile-8.0 because we want to disable default
REST implementation (Apache-CXF) provided by Liberty. We want to use Jersey
as our REST implementation because it better support multi-part streaming, -->
<!-- <feature>webProfile-8.0</feature> -->
<feature>jsp-2.3</feature>
<feature>cdi-2.0</feature>
<feature>managedBeans-1.0</feature>
<feature>jdbc-4.2</feature>
<!-- <feature>jaxrs-2.1</feature> -->
</featureManager>
Gradle build including jersey implementation
//JxRS Jersey implementation
compile group: 'org.glassfish.jersey.containers', name: 'jersey-container-servlet', version: '2.25.1'
compile group: 'org.glassfish.jersey.media', name: 'jersey-media-json-jackson', version: '2.25.1'
compile group: 'org.glassfish.jersey.media', name: 'jersey-media-multipart', version: '2.25.1'
compile group: 'com.fasterxml.jackson.jaxrs', name: 'jackson-jaxrs-json-provider', version: '2.9.0'
Extended jersey's ResourceConfig to configure my RestApplication
#ApplicationPath("/")
public class RestApplicationConfig extends ResourceConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(RestApplicationConfig.class);
public RestApplicationConfig() {
super();
configureResourcesAndFeatures();
}
private void configureResourcesAndFeatures() {
packages(RestApplicationConfig.class.getPackage().getName());
register(MultiPartFeature.class);
}
}
With all this setup my rest api works and I am able to make use of Jersey's multiple related classes in my code.
Now the problem is with CDI. In my resource class I am able to inject CDI managed resource/classes for example
#ApplicationScoped
#Path("/ping")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class PingResource {
private static final Logger LOGGER = LoggerFactory.getLogger(PingResource.class);
#Resource(lookup = "jndi_dpa_iss_rest_url")
private String issRestBaseUrlInResource;
#Inject
private DocumentService documentService;
}
In above class #Resource and #Inject are not able to resolve JNDI resource and managed bean. As soon as I enable jaxrs-2.1 feature in server.xml CDI injection works but then I loose jersey, it uses CXF.
DocumentService and its implementation class is defined as below. Everything is under same package as RestApplicationConfig class or it's sub-packages.
#ApplicationScoped
#Transactional(value = Transactional.TxType.NOT_SUPPORTED)
public class DocumentServiceImpl implements DocumentService {
// some code here
}
What do I need to use CDI in my rest resource classes?
Because there is no jersey extension for CDI 2.0 at the moment, I had to find workaround. Workaround is to manually query CDI container to the the type of bean we are interested in. This way we are manually injecting CDI bean in our resource class but the injected bean is managed bean instance so CDI has taken care of satisfying all its dependecies.
This we we are doing manual injection only in Resource layer but CDI should work fine for layer down.
Working code.
#ApplicationScoped
#Path("/ping")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class PingResource {
private DocumentService documentService = CDI.current().select(DocumentService.class).get();
}
Basically instead of #Inject manually query CDI container.

Swagger for Kotlin

Has anyone used a swagger tool with Kotlin?
In our organization, we have create most of our REST services using Java and SpringMVC (#RestController classes). We have used springfox to generate the Swagger API documentation. The swagger JSON representation is also used to automatically feed a searchable service catalog, so the swagger format for service metadata is important to us.
Some dev teams are now beginning to use Kotlin. We're looking for recommendations or comments related to using springfox or other swagger lib with Kotlin.
Here is sample spring boot app with swagger:
#RestController
class MyController {
#ApiOperation(value = "doc header...", notes = "detailed doc...")
#RequestMapping(value = "/double", method = arrayOf(RequestMethod.GET))
fun doubleValue(number: Int) = 2 * number
}
#Configuration
#EnableSwagger2
class SwaggerConfig {
#Bean
fun api(): Docket {
return Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
}
}
dependencies are
compile("io.springfox:springfox-swagger2:2.7.0")
compile("io.springfox:springfox-swagger-ui:2.7.0")
If you browse http://localhost:8080/swagger-ui.html it is all there...
I recently had a similar requirement. As a result I created a template project that integrates Kotlin, Webflux, and Swagger. It provides interactive API doc and automatic request validation.
See here -> https://github.com/cdimascio/kotlin-swagger-spring-functional-template
Validation is functional. It's used as such:
validate.request(req) {
// Do stuff e.g. return a list of names
ok().body(Mono.just(listOf("carmine", "alex", "eliana")))
}
with body
validate.request(req).withBody(User::class.java) { body ->
// Note that body is deserialized as User!
// Now you can do stuff.
// For example, lets echo the request as the response
ok().body(Mono.just(body))
}
It utilizes openapi 2 and 3 validation provided by atlassian.