Handling custom error code in spring cloud load balancer - spring-boot

In my current project, one of our microservices calls a REST Api of an another microservice and in an error scenario it returns a custom(469) error code.
I'm using a load balanced rest template in the calling service and its throwing an IllegalArgumentException error since 469 error code is not present in HttpStatus enum.
Is there any workaround to resolve this like using raw status codes instead of converting it into an enum?
java.lang.IllegalArgumentException: No matching constant for [469]
at org.springframework.http.HttpStatus.valueOf(HttpStatus.java:547)
at org.springframework.http.client.AbstractClientHttpResponse.getStatusCode(AbstractClientHttpResponse.java:33)
at org.springframework.cloud.client.loadbalancer.ResponseData.<init>(ResponseData.java:68)
at org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient.getClientResponse(BlockingLoadBalancerClient.java:120)
at org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient.execute(BlockingLoadBalancerClient.java:94)
at org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor.lambda$intercept$2(RetryLoadBalancerInterceptor.java:129)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:225)
at org.springframework.cloud.client.loadbalancer.RetryLoadBalancerInterceptor.intercept(RetryLoadBalancerInterceptor.java:79)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
at org.springframework.boot.actuate.metrics.web.client.MetricsClientHttpRequestInterceptor.intercept(MetricsClientHttpRequestInterceptor.java:86)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
at org.springframework.cloud.sleuth.instrument.web.mvc.TracingClientHttpRequestInterceptor.intercept(TracingClientHttpRequestInterceptor.java:69)
at org.springframework.cloud.sleuth.instrument.web.client.LazyTraceClientHttpRequestInterceptor.intercept(LazyTraceClientHttpRequestInterceptor.java:51)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:776)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:670)

I think we can manipulate the status coming from downstream service using interceptors.
Have you tried ClientHttpRequestInterceptor? Here is an example

Fixed in Spring cloud 2021.0.2 and above
https://github.com/spring-cloud/spring-cloud-commons/issues/1066
To use raw status codes instead of converting into an enum add the following property
spring.cloud.loadbalancer.use-raw-status-code-in-response-data: true

Related

WrongTargetException in Hazlecast

I have a multinode (2 nodes) setup of Weblogic 12c. In that I have deployed a spring boot application having microservices. The spring boot application is having the structure like Controller <--> Service <--> DAO. The #Cacheable annotation is used for few methods in "Service" classes/ layer. When endpoint it called, it reaches to controller and then it calls service class method. The issue is some of the calls are failing with below exception.
2023-01-15T14:55:31,122 ERROR [tuning)'] iServiceExceptionHandlerAdvice [, , ]
| Exception is encountered, message - WrongTarget! local: Member [XX.XX.XX.XX]:5981 - 9bXXX334-bf12-4XXb-afb6-XXXXXXX05442 this, expected-target: null, partitionId: 121, replicaIndex: 0, operation: com.hazelcast.map.impl.operation.GetOperation, service: hz:impl:mapService
com.hazelcast.spi.exception.WrongTargetException: WrongTarget! local: Member [XX.XX.XX.XX]:5981 - 9bXXX334-bf12-4XXb-afb6-XXXXXXX05442 this, expected-target: null, partitionId: 121, replicaIndex: 0, operation: com.hazelcast.map.impl.operation.GetOperation, service: hz:impl:mapService
at com.hazelcast.spi.impl.operationservice.impl.Invocation.newTargetNullException(Invocation.java:336) ~[hazelcast-3.12.10.jar:3.12.10]
at com.hazelcast.spi.impl.operationservice.impl.PartitionInvocation.newTargetNullException(PartitionInvocation.java:94) ~[hazelcast-3.12.10.jar:3.12.10]
at com.hazelcast.spi.impl.operationservice.impl.Invocation.initInvocationTarget(Invocation.java:289) ~[hazelcast-3.12.10.jar:3.12.10]
at com.hazelcast.spi.impl.operationservice.impl.Invocation.doInvoke(Invocation.java:614) ~[hazelcast-3.12.10.jar:3.12.10]
at com.hazelcast.spi.impl.operationservice.impl.Invocation.access$400(Invocation.java:96) ~[hazelcast-3.12.10.jar:3.12.10]
at com.hazelcast.spi.impl.operationservice.impl.Invocation$InvocationRetryTask.run(Invocation.java:827) ~[hazelcast-3.12.10.jar:3.12.10]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_141]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_141]
The above exception is from one node. Other node also fails intermittently with exact same exception, only the local: Member [XX.XX.XX.XX]:5982 will be different port like incremented by 1. This behavior goes away when I restart the deployed application (the spring boot application). It does not need to restart the complete weblogic server. Moreover this sometimes starts working without doing anything.
As per documentation of Hazelcast, https://docs.hazelcast.org/docs/3.7.8/manual/html-single/index.html says
WrongTargetException: Thrown when an operation is executed on the wrong machine, usually because the partition that operation belongs to has been moved to some other member.
There is no clear pattern to replicate this issue on local environment.
The alternatives I am thinking are
I can add try-catch in controller and log the details and throw some graceful exception. But still the data will be empty.
In try-catch in controller, I can call another method performing same operation but will not have #Cachable annotation. So that it will do everything same and fetch the data. put it in response and send it back.
Get rid of Hazelcast cache and use some other cache.
Is there any setting in Hazelcast which can do this (something similar to option 2) with some configuration. Instead of failing completely, it will do the invocation of method?

org.springframework.web.reactive.function.client.WebClient MalformedChunkCodingException: Bad chunk header

I have a spring boot application (2.2.6.RELEASE) which makes call to other Spring boot service through
org.springframework.web.reactive.function.client.WebClient which I build in the following manner :
final SslContext sslContext = buildSslContext(keyStorePassword, trustStore, keyStore);
final HttpClient httpClient = HttpClient.create().secure(ssl -> ssl.sslContext(sslContext));
return WebClient.builder().clientConnector(new ReactorClientHttpConnector(httpClient.wiretap(true)))
.exchangeStrategies(loggingExchangeStrategy()).build();
Normally this works fine. However I when I started performance testing the application using jmeter and bombarding it with requests, I randomly started getting failed responses with the following exception :
org.apache.http.MalformedChunkCodingException: Bad chunk header: {"order":{"status" {"status":"Success"}}}
at org.apache.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:274)
at org.apache.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:222)
at org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:183)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:135)
at org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:148)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.readResponse(HTTPSamplerBase.java:1905)
at org.apache.jmeter.protocol.http.sampler.HTTPAbstractImpl.readResponse(HTTPAbstractImpl.java:476)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:668)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:66)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1281)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1270)
at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:630)
at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:558)
at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:489)
at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:256)
at java.lang.Thread.run(Thread.java:748)
I also then got a similar exception while normally testing the application with the following exception :
"org.apache.http.ConnectionClosedException: Premature end of chunk coded message body: closing chunk expected\r\n\tat org.apache.http.impl.io.ChunkedInputStream.getChunkSize(ChunkedInputStream.java:268)\r\n\tat org.apache.http.impl.io.ChunkedInputStream.nextChunk(ChunkedInputStream.java:227)\r\n\tat org.apache.http.impl.io.ChunkedInputStream.read(ChunkedInputStream.java:186)\r\n\tat org.apache.http.conn.EofSensorInputStream.read(EofSensorInputStream.java:137)\r\n\tat sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)\r\n\tat sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)\r\n\tat sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)\r\n\tat
Since the exception dont seem to be from my code it seems that the problem is with WebClient. Is it?
It seems that it has something to do with Java, MalformedChunkCodingException
If this is the case then the issue seems to be the "transfer-encoding" header key with the value "chunked" and the HttpClient. Any idea anyone?
Also just for experimenting how can I override and downgrade or upgrade the HttpClient used by WebClient in the pom?

Netty doesn't allow pipe symbol | and throws Exception

My Endpoint is /mz/api/mot/pol/re/pt?Output=1|920222|R1205200130|Success|
I am using a Gateway application on Spring Boot which runs on Netty
and facing below exception,
java.lang.IllegalArgumentException: Illegal character in query at index 46:
at java.net.URI.create(URI.java:852) ~[na:1.8.0_242]
at reactor.netty.http.HttpOperations.resolvePath(HttpOperations.java:300) ~[reactor-netty-0.9.4.RELEASE.jar:0.9.4.RELEASE]
at reactor.netty.http.server.HttpServerOperations.<init>(HttpServerOperations.java:128) ~[reactor-netty-0.9.4.RELEASE.jar:0.9.4.RELEASE]
at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:157) ~[reactor-netty-0.9.4.RELEASE.jar:0.9.4.RELEASE]
at
The same error happens in Tomcat also and I solved it by setting relaxedQueryChars property. Kindly suggest how to do this. Thanks in advance.
You'll have to encode the pipes in your query string for netty. The "|" will become %7C
So this: /mz/api/mot/pol/re/pt?Output=1%7C920222%7CR1205200130%7CSuccess
Becomes: `/mz/api/mot/pol/re/pt?Output=1%7C920222%7CR1205200130%7CSuccess
There are lots of tools that will URL encode that string for you if you have control over the client side.
See also:
https://docs.oracle.com/javase/8/docs/api/java/net/URLEncoder.html

java.lang.NumberFormatException: For input string: "443,80"

I am getting the following exception while hitting my backend Spring boot application which is deployed on a Kubernetes container:
java.lang.NumberFormatException: For input string: "443,80"
All my services are registered with eureka:
#Eureka
spring.application.name=app-name
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=true
eureka.client.serviceUrl.defaultZone=http://app-eureka-dev/eureka
eureka.instance.preferIpAddress=true
eureka.instance.non-secure-port-enabled=true
And all my requests are routed through ingress/zuul services.
spring.application.name=app-gateway
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=true
eureka.client.serviceUrl.defaultZone=http://app-eureka-dev/eureka
When we try hitting backend services from the Swagger API, I am getting below exception.
java.lang.NumberFormatException: For input string: "443,80"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at springfox.documentation.swagger2.web.HostNameProvider.componentsFrom(HostNameProvider.java:72)
at springfox.documentation.swagger2.web.Swagger2Controller.getDocumentation(Swagger2Controller.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod
I am connecting to the eureka service through the container name, even though I am getting above exception. Is there any other configuration required, as we do ssl offloading in ingress, rest should be plain http or unsecure calls within the container services.
This should have been fixed with Springfox 2.7.0, as can be seen in this GitHub issue and the release notes of this version.
Before Springfox 2.7.0, the following code was used to determine the port number in HostNameProvider:
String port = request.getHeader("X-Forwarded-Port");
if (hasText(port)) {
builder.port(Integer.parseInt(port));
}
So basically it used the X-Forwarded-Port header to determine the port number. However, in your case it seems like it passes both the HTTP and HTTPS ports (443,80), which is obviously not a valid integer.
Upgrading your springfox-swagger2 dependency to 2.7.0 (or higher) should do the trick.
I experienced the same problem in SWAGGER when my DTO properties annotated with #APIModelProerty. Majorly, the problem occurred when Long type properties are present in the DTO object and failed to convert the empty string ("") value into 0.
So, it solved when I added example property in #APIModelProperty(example = "1", required = "false").

How to use Spring Hatoas ControllerLinkBuilder for a Thymeleaf templated scheduled email

I'm using Spring Hateoas in a Boot app to avoid manual creation of links in the view. It works great in Thymeleaf views, it works when a controller calls a service to send an email that is also rendered by Thymeleaf.
The code to create link is pretty standard
this.readLink = linkTo(methodOn(PostController.class)
.readPost(eventId, postId))
.withRel("ReadPost");
But for a #Scheduled service generated email, it fails like this
015-08-23 22:28:40.886 ERROR 1180 --- [pool-2-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task.
java.lang.IllegalStateException: Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler?
at org.springframework.util.Assert.state(Assert.java:392) ~[spring-core-4.2.0.RELEASE.jar:4.2.0.RELEASE]
at org.springframework.hateoas.mvc.ControllerLinkBuilder.getCurrentRequest(ControllerLinkBuilder.java:242) ~[spring-hateoas-0.18.0.RELEASE.jar:na]
at org.springframework.hateoas.mvc.ControllerLinkBuilder.getBuilder(ControllerLinkBuilder.java:189) ~[spring-hateoas-0.18.0.RELEASE.jar:na]
at org.springframework.hateoas.mvc.ControllerLinkBuilderFactory.linkTo(ControllerLinkBuilderFactory.java:121) ~[spring-hateoas-0.18.0.RELEASE.jar:na]
Is there anything I can do to get around the lack of an HttpServletRequest due to the code running as a #Scheduled job?
ControllerLinkBuilder currently can only be used from within a request as only that allows it to create a fully qualified link using server and port information from it.
Within an #Scheduled-invoked method that information is not available. If you provide more information on what you're actually creating in that very method, I can suggest workarounds.

Resources