WebClient is incorrectly trying to start a web server - spring-boot

I am trying to migrate my REST client application to use WebClient instead of RestTemplate.
But, when I run my client code, it is apparently trying to start a web server and connect to port 8080, which fails because Tomcat is already running on the port.
I don't want to start a web server. I just want to connect to an external web server and pull back a response.
Here is the error I get:
***************************
APPLICATION FAILED TO START
***************************
Description:
Web server failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
Here is my test code:
package test.rest.webClient;
import java.util.Map;
import org.apache.hc.core5.net.URIBuilder;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.RequestHeadersUriSpec;
import org.springframework.web.reactive.function.client.WebClient.ResponseSpec;
import reactor.core.publisher.Mono;
import test.Path;
#SpringBootApplication
public class WebClientTest implements CommandLineRunner {
#Override
public void run(String... args)
throws Exception {
URIBuilder builder = new URIBuilder();
builder.setScheme("https");
builder.setHost("marketing.propfinancing.com");
builder.setPath("/caddata/TXCollin/getByIdAndYear");
builder.addParameter("id", "37");
builder.addParameter("year", "2022");
WebClient client = WebClient.create();
RequestHeadersUriSpec<?> uriSpec = client.get();
uriSpec.uri(builder.build());
uriSpec.header(Path.getApplicationProperties().getProperty("caddata.apiKey.header.name"),
Path.getApplicationProperties().getProperty("caddata.apiKey"));
uriSpec.accept(MediaType.APPLICATION_JSON);
ResponseSpec responseSpec = uriSpec.retrieve();
ParameterizedTypeReference<Map<String,Object>> typeReference = new ParameterizedTypeReference<Map<String,Object>>(){};
Mono<Map<String,Object>> mono = responseSpec.bodyToMono(typeReference);
Map<String,Object> response = mono.block();
for( Object key : response.keySet() ) {
Object value = response.get(key);
LoggerFactory.getLogger(getClass()).warn(key+":"+value);
}
}
public static void main(String[] args)
throws Exception {
SpringApplication.run(WebClientTest.class, args);
}
}
Any ideas?

Per default spring boot starts up either as a web application or a reactive application depending on what libraries you have on your classpath.
But you can also tell the framwork to not start up the webserver by explicitly setting the WebApplicationType to None
here is an example:
new SpringApplicationBuilder(MainApplication.class)
.web(WebApplicationType.NONE)
.run(args);
or you can set it in the application properties:
spring.main.web-application-type=none
You can read more about it here:
17.1.5. Create a Non-web Application
Spring Boot no web server

Related

Spring Boot Actuator to show service start datetime

I am using spring-boot-starter-actuator in my project wanted to show service starts date time along with other information on /info end point.
Please guide how to achieve this.
Thanks in advance
/startup - is the Actuator Endpoint to see the startup information.
Sample URL syntax:
http://<HOST>:<port>/actuator/startup
For more info, visit here
--- Edit---
/startup endpoint does not get exposed by default, Hence need to enable explicitly by below property in application.properties :
management.endpoints.web.exposure.include=startup
BufferingApplicationStartup class is in-memory buffered implementation for capturing startup steps. Hence in the main class below changes are required:
import java.util.TimeZone;
import javax.annotation.PostConstruct;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
#SpringBootApplication
public class ActuatorEndpointApplication {
public static void main(String[] args) {
// SpringApplication.run(ActuatorEndpointApplication.class, args);
SpringApplication app = new SpringApplication(ActuatorEndpointApplication.class);
app.setApplicationStartup(new BufferingApplicationStartup(2048));
app.run(args);
}
}
Now test the url:
http://<HOST>:<port>/actuator/startup in Chrome/Postman.
For more info, refer here

How do you configure HTTP connector attributes when using the embedded Tomcat in Spring Boot

By default when uploading a file (as a PUT request) to Tomcat it will read the headers and then if the client sends a Expect: 100-continue header it will straight away reply back with the HTTP/1.1 100 response to prompt the client to send the file. This can be controlled on the connector with the continueResponseTiming attribute outlined in the HTTP connector documention
However in Spring Boot this settings can't easily be configured with application properties.
What is the best way to configure attributes like this?
By using a TomcatConnectorCustomizer bean, here's a simple example:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean
public TomcatConnectorCustomizer connectorCustomizer() {
return connector -> connector.setProperty("continueResponseTiming", "onRead");
}
}

Spring Boot REST API not reachable in GCP

I am deploying the Spring Boot Rest API in Google Cloud Platform. The Rest API is running fine in my local while running the application as Spring boot application. But the same REST API URL is not recognized while deploying in GCP.
Project Structure:
app.yaml:
runtime: java
env: flex
runtime_config:
  jdk: openjdk8
handlers:
- url: /.*
script: this field is required, but ignored
GreetingController.java
package com.designdreamers.rest.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.designdreamers.rest.vo.Greeting;
#RestController
public class GreetingController {
private int idValue;
#RequestMapping(value="/greet")
public Greeting greeting(#RequestParam(value="content", defaultValue = "Hello World!!")String content){
Greeting greeting=new Greeting();
greeting.setId(idValue++);
greeting.setContent(content);
return greeting;
}
}
Application.java
package com.designdreamers.rest.main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan(basePackages = "com.designdreamers.rest.controller")
public class Application {
public static void main(String args[]){
SpringApplication.run(Application.class,args);
}
}
Below is the REST API response that I get on running the application as Spring boot application in local.
URL :http://localhost:8080/greet
{"id":1,"content":"Hello World!!"}
I deployed the same application in GCP and on hitting the REST API URI, below is the error that we get.
URI: http://helloworld-001.appspot.com/greet
Error: Not Found
The requested URL /greet was not found on this server.
A helping hand on identifying the issue would be very helpful.

How to provide custom SSLContext to Netty server on spring boot

How can we configure a custom SSLContext to a spring boot application with Netty server?
From the source code, I see 'reactor.ipc.netty.http.server.HttpServerOptions' which are some server startup options, but I don't find a way to configure them.
Is there any handler through which we can inject our custom SSLContext?
I am looking something similar to this (Spring 5 WebClient using ssl) where WebClient is configured with a custom SSLContext through 'reactor.ipc.netty.http.client.HttpClientOptions'.
Netty can be customized like blow example in spring-boot 2.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
/**
* author : Mohammad Ghoreishi
*/
#Configuration
#ImportResource({"classpath:convert-iban-service.xml", "classpath:config-loader-context.xml", "classpath*:error-resolver.xml"})
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> customizer(){
return new WebServerFactoryCustomizer<NettyReactiveWebServerFactory>() {
#Override
public void customize(NettyReactiveWebServerFactory factory) {
Ssl ssl = new Ssl();
// Your SSL Cusomizations
ssl.setEnabled(true);
ssl.setKeyStore("/path/to/keystore/keystore.jks");
ssl.setKeyAlias("alias");
ssl.setKeyPassword("password");
factory.setSsl(ssl);
factory.addErrorPages(new ErrorPage("/errorPage"));
}
};
}
}

Hystrix Dashboard Issue in Spring Boot

I am new to Hystrix Dashboard. I have written sample application with Hystrix.
I want to see the Hystrix chart (command metric stream). But I am getting the below error:
Circuit: Unable to connect to Command Metric Stream
Thread Pools: Loading...
I am using STS with Maven.
Below is the code used:
Simple server microservice application (Spring boot web running in port 8085)
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
#RestController
#SpringBootApplication
public class BookstoreApplication {
#RequestMapping(value = "/recommended")
public String readingList(){
return "Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)";
}
public static void main(String[] args) {
SpringApplication.run(BookstoreApplication.class, args);
}
}
Simple client microservice application (Spring boot web running in port 8095) I have included the dependency of Hystrix and Hystrix Dashboard along with Web, so all the Hystrix dependencies are in classpath
package hello;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
#Service
public class BookService {
private final RestTemplate restTemplate;
public BookService(RestTemplate rest) {
this.restTemplate = rest;
}
#HystrixCommand(fallbackMethod = "reliable")
public String readingList() {
URI uri = URI.create("http://localhost:8090/recommended");
return this.restTemplate.getForObject(uri, String.class);
}
public String reliable() {
return "Cloud Native Java (O'Reilly)";
}
}
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.web.client.RestTemplate;
#EnableHystrixDashboard
#EnableHystrix
#EnableCircuitBreaker
#RestController
#SpringBootApplication
public class ReadingApplication {
#Autowired
private BookService bookService;
#Bean
public RestTemplate rest(RestTemplateBuilder builder) {
return builder.build();
}
#RequestMapping("/to-read")
public String toRead() {
return bookService.readingList();
}
public static void main(String[] args) {
SpringApplication.run(ReadingApplication.class, args);
}
}
By running the above code, the hystrix is working fine, when the BooKStoreApplication is down, it is going to fallback method.
Both the urls are working fine.
Normal Case:
http://localhost:8085/recommended
Output: Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)
http://localhost:8095/to-read
Output: Spring in Action (Manning), Cloud Native Java (O'Reilly), Learning Spring Boot (Packt)
When BookStoreApplication is down (http://localhost:8085/recommended) accessing http://localhost:8095/to-read returns "Cloud Native Java (O'Reilly)" as expected.
But when I tried to invoke this url http://localhost:8095/hystrix, I am getting the Hystrix DashBoard Page and asking for the stream value.
I have tried given http://localhost:8095/ or http://localhost:8095/to-read, and clicked "Monitor Stream" and it is going to next page with error:
Circuit: Unable to connect to Command Metric Stream
Thread Pools: Loading...
I've experienced the same. The main problem was, that I didn't have the actuator dependency in my maven pom. So I could not get the hystrix stream.
Include the spring-boot-actuator.
Check if localhost:8085/health is running.
Try to enter localhost:8085/hystrix.stream to stream value in Hystrix Dashboard.
Execute the service few times -> the dashboard should show the monitored method/command.

Resources