CoroutineExceptionHandler does not catch exception - kotlin-coroutines

I run this code
fun main(args: Array<String>) = runBlocking {
val exceptionHandler = CoroutineExceptionHandler { _, _ ->
println("handled")
}
val job = launch(exceptionHandler) {
throw java.lang.RuntimeException("foobar")
}
job.join()
println("Finished")
}
but the foobar exception is not handled and handled is not printed to the console
Exception in thread "main" java.lang.RuntimeException: foobar
at MainKt$main$1$outer$1.invokeSuspend(Main.kt:10)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at MainKt.main(Main.kt:3)
Why is this?

Looks like the job is not the parent job for some reason. Only the parent's handler will handle exceptions of it's children. So we need to define a scope and initialize it with a job. This job will be the parent job.
fun f2() = runBlocking {
val exceptionHandler = CoroutineExceptionHandler { _, _ ->
println("handled")
}
val cs = CoroutineScope(Job() + exceptionHandler)
val job = cs.launch {
throw java.lang.RuntimeException("foobar")
}
job.join()
}

Related

Constructor parameter is null when referred to in an overrided method

My code is written in Kotlin. I have a config class defined in a file along 2 more classes as below:
#Configuration
class MultipartConfig(private val multipartProperties: MultipartProperties) {
#Bean
fun multipartResolver(): StandardServletMultipartResolver {
val multipartResolver = MultipartResolver(multipartProperties)
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily)
return multipartResolver
}
}
class MultipartResolver(private val multipartProperties: MultipartProperties) :
StandardServletMultipartResolver() {
override fun resolveMultipart(request: HttpServletRequest): MultipartHttpServletRequest {
return MultipartHttpServletRequest(multipartProperties, request)
}
}
class MultipartHttpServletRequest(
private val multipartProperties: MultipartProperties, request: HttpServletRequest
) : StandardMultipartHttpServletRequest(request, multipartProperties.isResolveLazily) {
override fun handleParseFailure(ex: Throwable) {
val msg = ex.message
if (msg != null && msg.contains("size") && msg.contains("exceed")) {
throw MaxUploadSizeExceededException(multipartProperties.maxFileSize.toMegabytes(), ex)
}
throw MultipartException("Failed to parse multipart servlet request", ex)
}
}
When I debug this code, in the class MultipartHttpServletRequest, constructor property multipartProperties is NOT null but the same property in the throw MaxUploadSizeExceededException(multipartProperties.maxFileSize.toMegabytes(), ex) is ALWAYS null. I cannot understand why this is happening.
Could someone please explain why this is happening?
I'm just answering my own question for clarity. I figured out the issue. It was related to the constructor of that class StandardMultipartHttpServletRequest. Below is the code of the constrcutor.
public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
throws MultipartException {
super(request);
if (!lazyParsing) {
parseRequest(request);
}
}
Now, inside of parseRequest there is a catch block (for brevity I'm not posting the whole code of the method)
catch (Throwable ex) {
handleParseFailure(ex);
}
When the constructor of the super class is throwing exception the child's constructor will not get a chance to initialize.

WebSocketClient frozen when connecting to WebSocket (Spring WebFlux)

I have written a sample to demo client/server communication with WebSocket protocol.
The server code:
#SpringBootApplication
class WebSocketServerApplication {
#Bean
fun webSocketMapping(mapper: ObjectMapper): HandlerMapping? {
val map = mapOf("/ws/messages" to ChatSocketHandler(mapper))
val simpleUrlHandlerMapping = SimpleUrlHandlerMapping().apply {
urlMap = map
order = 10
}
return simpleUrlHandlerMapping
}
#Bean
fun handlerAdapter(): WebSocketHandlerAdapter = WebSocketHandlerAdapter()
}
fun main(args: Array<String>) {
runApplication<WebSocketServerApplication>(*args)
}
class ChatSocketHandler(val mapper: ObjectMapper) : WebSocketHandler {
val sink = Sinks.replay<Message>(100);
val outputMessages: Flux<Message> = sink.asFlux();
override fun handle(session: WebSocketSession): Mono<Void> {
println("handling WebSocketSession...")
session.receive()
.map { it.payloadAsText }
.map { Message(id= UUID.randomUUID().toString(), body = it, sentAt = Instant.now()) }
.doOnNext { println(it) }
.subscribe(
{ message: Message -> sink.next(message) },
{ error: Throwable -> sink.error(error) }
);
return session.send(
Mono.delay(Duration.ofMillis(100))
.thenMany(outputMessages.map { session.textMessage(toJson(it)) })
)
}
fun toJson(message: Message): String = mapper.writeValueAsString(message)
}
data class Message #JsonCreator constructor(
#JsonProperty("id") var id: String? = null,
#JsonProperty("body") var body: String,
#JsonProperty("sentAt") var sentAt: Instant = Instant.now()
)
I have provided a client written in Angular, it works well, the codes is here.
When trying to a test for the server.
#SpringBootTest()
class WebsocketServerApplicationTests {
lateinit var client: WebSocketClient;
#Autowired
lateinit var mapper: ObjectMapper;
#BeforeEach
fun setup() {
this.client = ReactorNettyWebSocketClient()
}
#Test
fun contextLoads() {
val replay = Sinks.replay<Message>(10)
client.execute(
URI("ws://localhost:8080/ws/messages")
) { session: WebSocketSession ->
println("Starting to send messages")
session.receive()
.map { mapper.readValue(it.payloadAsText, Message::class.java) }
.subscribe { replay.next(it) }
session.send(
Mono.delay(Duration.ofSeconds(1)).thenMany(
Flux.just("test message", "test message2")
.map(session::textMessage)
)
).then()
}.subscribe()
StepVerifier.create(replay.asFlux().takeLast(2))
.consumeNextWith { it -> assertThat(it.body).isEqualTo("test message") }
.consumeNextWith { it -> assertThat(it.body).isEqualTo("test message2") }
.verifyComplete()
}
}
When starting up the application, run the test, it is frozen, not work as expected.
The problem is on the test side.
Complete your stream to make takeLast(n) working
First of all, you expect to take the last 2 elements from the stream. However, that is going to happen when and only when there is onComplete signal, which let the Flux.takeLast know that there is the end of the stream, so the last n observed elements are last.
In your code, you listen to the WebsocketInbound messages and send them to the ReplaySink. However, the FluxSink#complete message is never called, which means takeLast(2) will hang forever as expected.
Solution
On the one hand, the solution seems to be obvious:
session.receive()
.map { mapper.readValue(it.payloadAsText, Message::class.java) }
.subscribe ({ replay.next(it) }, { replay.error(it) }, { replay.complete() })
However, there is might be a trick:
.receive sends a terminal signal only when the WebSocket connection is closed.
Therefore, in order to receive a terminal signal, please ensure that the server closes the connection on its side. Otherwise, the test will still hang waiting for the final terminal signal.
If the connection close is not desired, try to simply use .take(2).
Finally fixed this issue myself after reading some posts on stackoverflow and source codes of testing reactive WebSocket in the spring framework.
Spring reactive ReactorNettyWebSocketClient not logging anything
How to use Spring Reactive WebSocket and transform it into the Flux stream?
WebSocketIntegrationTests
#SpringBootTest()
class WebSocketServerApplicationTests {
lateinit var client: WebSocketClient
#Autowired
lateinit var mapper: ObjectMapper
#BeforeEach
fun setup() {
this.client = ReactorNettyWebSocketClient()
}
#Test
fun contextLoads() {
val replay = Processors.replay<Message>(100)
try {
client.execute(
URI("ws://localhost:8080/ws/messages")
) { session: WebSocketSession ->
val receiveMono = session.receive()
.map { mapper.readValue(it.payloadAsText, Message::class.java) }
.log("received from server::")
.subscribeWith(replay)
.then()
session
.send(
Mono.delay(Duration.ofMillis(500)).thenMany(
Flux.just("test message", "test message2")
.map(session::textMessage)
)
)
.then(receiveMono)
}.block(Duration.ofSeconds(5L))
// assert
assertThat(replay.blockLast(Duration.ofSeconds(5L))?.body).isEqualTo("test message2")
} catch (e: Exception) {
println(e.message)
}
}
}

Spring Kafka commit reset offset not work when application goes down

As described in the documentation, an offset should only be committed when I actually commit (When AckMode.MANUAL_IMMEDIATE or AckMode.MANUAL) or at the end of the listener execution when AckMode.RECORD, however, in the middle of processing the method annotated with #KafkaListener the application goes down, the message is not re-delivered, the application starts reading from the next valid message and this current message is lost (message that was being processed when the application was restarted), how do I achieve the goal of the application reprocessing an uncommitted message when the application is restarted in the middle of processing? I have also tried configuring AUTO_OFFSET_RESET_CONFIG as earliest, latest and none without success in the 3 models. For testing purposes, I created a topic with just one partition, I forced the listener to use the container factory that I define manually.
The springboot-version 2.2.6
#Configuration
class KafkaTestConfiguration {
#Bean
fun producerFactory(): ProducerFactory<String, String> {
return DefaultKafkaProducerFactory(producerConfigs())
}
#Bean
fun consumerFactory(): ConsumerFactory<Any, Any> {
return DefaultKafkaConsumerFactory(consumerConfigs())
}
#Bean
fun producerConfigs(): Map<String, Any> {
val props: MutableMap<String, Any> = HashMap()
props[ProducerConfig.BOOTSTRAP_SERVERS_CONFIG] = "localhost:9094"
props[ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java
props[ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG] = StringSerializer::class.java
return props
}
#Bean
fun consumerConfigs(): Map<String, Any> {
val props: MutableMap<String, Any> = HashMap()
props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = "localhost:9094"
props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java
props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java
props[ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG] = 20000
props[ConsumerConfig.GROUP_ID_CONFIG] = "kafka-retry"
props[ConsumerConfig.AUTO_OFFSET_RESET_CONFIG] = "earliest"
props[ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG] = "false"
return props
}
#Bean
fun kafkaTemplate(): KafkaTemplate<String, String> {
return KafkaTemplate(producerFactory())
}
#Bean
fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<Any, Any> {
val factory: ConcurrentKafkaListenerContainerFactory<Any, Any> = ConcurrentKafkaListenerContainerFactory()
factory.consumerFactory = consumerFactory()
factory.consumerFactory.createConsumer()
val containerProperties = factory.containerProperties
containerProperties.isAckOnError = false
containerProperties.ackMode = AckMode.MANUAL_IMMEDIATE
containerProperties.commitLogLevel = LogIfLevelEnabled.Level.INFO
containerProperties.isLogContainerConfig = true
return factory
}
#Component
class KafkaListenerAck {
#KafkaListener(id = "listMsgAckConsumer", topics = ["kafkaListenerTest1"],
groupId = "kafka-retry",
concurrency = "1",
containerFactory = "kafkaListenerContainerFactory"
)
fun onMessage(data: ConsumerRecord<String, String>, acknowledgment: Acknowledgment?) {
println("listMsgAckConsumer1 - topic ${data.topic()} offset ${data.offset()} partition ${data.partition()} message ${data.value()}")
println("If stop container here, the next pool will not deliver the current unconfirmed message")
acknowledgment?.acknowledge()
}
}
The offset will not be committed until the acknowledgment.acknowledge() is called. Set the commitLogLevel container property to DEBUG to see commit activity.
auto.offset.reset only applies if the consumer has never committed an offset (new consumer groups only).
If you can't figure it out from the log; edit the question with the log snippet.

How to use feign interceptor / decoder to log request - response in custom format?

I'm developing a custom logging framework for springboot to log rest-template requests and response and is working fine. Am trying to implement the same for 'Feign-Client' and am faced with couple of issues.
For request logging, am leveraging FeignRequestInterceptor and it is working fine, only problem here is I cannot retrieve the full request URL.
Below method is giving me only relative URL.
requestTemplate.url()
To log the response, only way i could find was the ResponseDecoder. There I'm able to retrieve everything other than the payload. When accessing the payload from
InputStream is = response.body().asInputStream();
String payload = new String(IOUtils.toByteArray(is));
This method works, but the original stream is closed because of which logging happens fine, but client is throwing exception when returning response.
'trying to open closed stream'
I would like suggestions if there are better ways of logging request response in Feign similar to spring rest-template. Or if the method I have adopted is fine, help me resolve the problems above.
You can configure a custom feign.Logger instance to handle this. There are two built in, JavaLogger which uses java.util.logging and Slf4JLogger that uses slf4j. You can create your own logger implementation by extending feign.Logger and registering it as a #Bean.
That logger should be picked up by Spring and registered with your FeignClient. Here is the Logger base class to get you started:
protected abstract void log(String configKey, String format, Object... args);
Create your own instance, implement this method and it will be called before the request and after the response is returned. No need to update the interceptor or create a response decoder.
in your RestConfiguration you need to up default level of logging feignClient and override by #Bean feignLogger like:
#Configuration(proxyBeanMethods = false)
#EnableCircuitBreaker
#EnableFeignClients(basePackageClasses = [Application::class])
class RestConfiguration: WebMvcConfigurer {
#Bean
fun feignLoggerLevel(): Logger.Level {
return Logger.Level.FULL
}
#Bean
fun feignLogger(): Logger {
return FeignClientLogger()
}
}
and implement your logger (logbook format):
import feign.Logger
import feign.Request
import feign.Response
import feign.Util.*
import org.slf4j.LoggerFactory
class FeignClientLogger : Logger() {
private val log = LoggerFactory.getLogger(this::class.java)
override fun logRequest(configKey: String?, logLevel: Level?, request: Request?) {
if (request == null)
return
val feignRequest = FeignRequest()
feignRequest.method = request.httpMethod().name
feignRequest.url = request.url()
for (field in request.headers().keys) {
for (value in valuesOrEmpty(request.headers(), field)) {
feignRequest.addHeader(field, value)
}
}
if (request.requestBody() != null) {
feignRequest.body = request.requestBody().asString()
}
log.trace(feignRequest.toString())
}
override fun logAndRebufferResponse(
configKey: String?,
logLevel: Level?,
response: Response?,
elapsedTime: Long
): Response? {
if (response == null)
return response
val feignResponse = FeignResponse()
val status = response.status()
feignResponse.status = response.status()
feignResponse.reason =
(if (response.reason() != null && logLevel!! > Level.NONE) " " + response.reason() else "")
feignResponse.duration = elapsedTime
if (logLevel!!.ordinal >= Level.HEADERS.ordinal) {
for (field in response.headers().keys) {
for (value in valuesOrEmpty(response.headers(), field)) {
feignResponse.addHeader(field, value)
}
}
if (response.body() != null && !(status == 204 || status == 205)) {
val bodyData: ByteArray = toByteArray(response.body().asInputStream())
if (logLevel.ordinal >= Level.FULL.ordinal && bodyData.isNotEmpty()) {
feignResponse.body = decodeOrDefault(bodyData, UTF_8, "Binary data")
}
log.trace(feignResponse.toString())
return response.toBuilder().body(bodyData).build()
} else {
log.trace(feignResponse.toString())
}
}
return response
}
override fun log(p0: String?, p1: String?, vararg p2: Any?) {}
}
class FeignResponse {
var status = 0
var reason: String? = null
var duration: Long = 0
private val headers: MutableList<String> = mutableListOf()
var body: String? = null
fun addHeader(key: String?, value: String?) {
headers.add("$key: $value")
}
override fun toString() =
"""{"type":"response","status":"$status","duration":"$duration","headers":$headers,"body":$body,"reason":"$reason"}"""
}
class FeignRequest {
var method: String? = null
var url: String? = null
private val headers: MutableList<String> = mutableListOf()
var body: String? = null
fun addHeader(key: String?, value: String?) {
headers.add("$key: $value")
}
override fun toString() =
"""{"type":"request","method":"$method","url":"$url","headers":$headers,"body":$body}"""
}

Validation and DDD - kotlin data classes

In Java I would do validation when creating constructor in domain object, but when using data class from kotlin I don't know how to make similar validation. I could do that in application service, but I want to stick to domain object and it's logic. It's better to show on example.
public class Example {
private String name;
Example(String name) {
validateName(name);
this.name = name;
}
}
In Kotlin I have just a data class is there a way to do it similarly to Java style?
data class Example(val name: String)
You can put your validation code inside an initializer block. This will execute regardless of whether the object was instantiated via the primary constructor or via the copy method.
data class Example(val name: String) {
init {
require(name.isNotBlank()) { "Name is blank" }
}
}
A simple example:
fun main() {
println(Example(name = "Alice"))
println(try { Example(name = "") } catch (e: Exception) { e })
println(try { Example(name = "Bob").copy(name = "") } catch (e: Exception) { e })
}
Produces:
Example(name=Alice)
java.lang.IllegalArgumentException: Name is blank
java.lang.IllegalArgumentException: Name is blank
You can get a similar effect by using companion factory method
:
data class Example private constructor(val name: String) {
companion object {
operator fun invoke(name: String): Example {
//validateName
return Example(name)
}
}
}
...
val e = Example("name")
e.name //validated
You may want to use the interface to hide the data class.
The amount of code will increase slightly, but I think it's more powerful.
interface Example {
val id: String
val name: String
companion object {
operator fun invoke(name: String): Example {
// Validate ...
return ExampleData(
id = UUID.randomUUID().toString(),
name = name
)
}
}
fun copy(name: String): Example
operator fun component1() : String
operator fun component2() : String
}
private data class ExampleData(override val id: String, override val name: String): Example {
override fun copy(name: String): Example = Example(name)
}

Resources