Spring boot - Rest Call client without embedded tomcat - java

I have been trying to figure out an issue with spring boot and as i am new to spring I thought of getting some help here.
I have a spring boot based java application which runs as a daemon and makes some GET request to a remote server. (Acts only as a client).
But my spring boot application internally starts an embedded tomcat container.
My understanding is that if the java app acts as a server, it would need tomcat. But my application being only a consumer of remote machine's GET APIs, why would it need an embedded tomcat ?
In my pom file I have specified spring-boot-starter-web,
on assumption that it is needed for even making GET calls.
But after doing some research on disabling embedded tomcat, I found a solution.
To make following changes,
#SpringBootApplication(exclude = {EmbeddedServletContainerAutoConfiguration.class,
WebMvcAutoConfiguration.class})
&
in application.yml
spring:
main:
web-environment: false
With the application.yml changes, my jar is not even getting started, aborts directly, without even logging anything in logback logs.
Now, if i remove the application.yml change, my jar starts (only with first change in #SpringBootApplication anno.) but goes into some exception.
[main] o.s.boot.SpringApplication : Application startup failed
org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
My Doubts here are,
1) Is tomcat, be it standalone or embedded, really needed for a application which just makes GET API calls to remote machine ?
2) How do i overcome this exception and safely remove the embedded tomcat and still perform the GET API calls ?

You seem to be on completely the wrong track here, starting from a web application template and then trying to turn off the web application aspect.
Far better to start from a regular commandline client template and go from there, as detailed in the relevant Spring Guide.
Basically the application reduces to
#SpringBootApplication
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String args[]) {
SpringApplication.run(Application.class);
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
#Bean
public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
Quote quote = restTemplate.getForObject(
"http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
log.info(quote.toString());
};
}
}
And the pom to
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>

I had this problem. All I wanted was to have a Client making REST requests. Unfortunately I had a dependency which was embedding Jetty, and Jetty was always started.
In order to disable Jetty all I needed to do was to add in applications.properties the following entry:
spring.main.web-application-type=none
That fixed it.

Here is the most simple solution for me, make spring boot application just a restful api consumer.
Replace the dependence
implementation("org.springframework.boot:spring-boot-starter-web")
with
implementation("org.springframework.boot:spring-boot-starter-json")
RestTemplate and jackson are available in the project without embedded tomcat.

Answering your questions:
1) embedded by defaut - not needed for clients HTTP requests;
2) You can use CommandLineRunner for spring boot applications without any web:
#SpringBootApplication
public class SpringBootConsoleApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringBootConsoleApplication.class, args);
}
#Override
public void run(String... args) {
// TODO: start you thread here to execute client HTTP REST requests (or any other job you need)
}
}
This will disable web completelly - no issues with manual miss-configuration.
Here is some docs:
http://www.baeldung.com/spring-boot-console-app
You also need replase spring-boot-starter-web dependency with spring-boot-starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

From your question, i assume you want your application to keep running in background and it makes some get calls in it's life cycle. If that's the case, then
answering your first question, yes, you need an embedded tomcat or
jetty or need to deploy your application to an external application
server.
Second, to get rid of the exception your facing, don't exclude
EmbeddedServletContainerAutoConfiguration and
WebMvcAutoConfiguration class as it's needed for default embedded
tomcat auto configuration.

Related

How to start a jax-rs server?

I have my endpoints like so:
#Path("/users")
public class Users {
private final SomeDependency dependency
public Users(SomeDependency dependency) {
this.dependency = dependency;
}
#GET
#Path("/{id}")
public Response Get(#PathParam("id") int id) {
User user = this.dependency.get(id);
return Response.ok(user).build();
}
}
Now how do I actually run a server with this endpoint?
I am confused about web servers/applications/servlets, using jersey jetty glassfish or whatever. Also web.xml files? what? ResourceConfigs only accept classes, not their instances. Everything seems to be such a mess.
How can I just do something similar to this?
public static void main(String[] args) throws Exception {
SomeDependency dependency = new SomeDependency();
Users usersEndpoint = new Users(dependency);
Server server = new Server();
server.registerEndpoint(usersEndpoint);
server.start();
}
Server
As far as the server goes, you have two types you have to consider: installed or embedded.
Installed
An installed server is one that is installed on your machine. For example, if you download Tomcat 10 and then follow the installation instruction, the server will get installed on your machine at whatever location you choose. In this situation, when you have an app (a war file) you are ready to deploy, you will copy it to your Tomcat server (to a specific location) and on server startup, Tomcat will see the application and then run it. The applications running in this type of environment will require a web.xml as they are deployed in a war file.
Embedded
An embedded server is one that is packaged into your jar application and is started in a main method just like any other Java SE application. Most installed servers also come with an embedded version. All you need to do is add the server dependencies into your application and then write the server configuration and startup (and shutdown) code. Applications running in this environment will not require a web.xml as they are deployed as a jar file.
Which one to use?
With the advancement and popularity of cloud services and microservices, many applications are moving towards embedded servers. The reason is that they are easy to deploy, are scalable, relatively lightweight, and applications become more self contained. There are many other pros that come with using embedded servers, but there are also some cons. Do your research before you make your final decision about which one you should use.
Example
If you want to quickly get started without having to worry about setting up the project, knowing what dependencies you need to add, adding the startup code, an easy way to get up and running is to use a Maven Archetype. If you go to the Jersey Docs, they get you started with an embedded Grizzly server running a Jersey application. What you need to do is run the following command from the command line (assuming you have Maven installed in your machine)
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example \
-DarchetypeVersion=2.33
If you read the docs I linked to, it will give an explanation of all that comes with the initial application. The main class will look like the following (with comments and imports omitted)
public class Main {
public static final String BASE_URI = "http://localhost:8080/myapp/";
public static HttpServer startServer() {
final ResourceConfig rc = new ResourceConfig().packages("org.example");
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
System.in.read();
server.stop();
}
}
In this code, they use the packages() method of the ResourceConfig class that specifies a package for Jersey to scan for #Path and #Provider classes to register. In your case, if you don't need that, you can remove the method call. If you want to register your Users instance, all you do is call register(users) on the ResourceConfig.
See Also
What exactly is the ResourceConfig class in Jersey 2 - this link will help you get started with the ResourceConfig class and show you how you can register you resource classes or instances.
Other Frameworks
There are other frameworks out there that you may also want to consider
Dropwizard - Dropwizard is an opinionated framework built on top of Jersey. It uses an embedded Jetty as its server and comes with many other features to make developing your applications easier. They have pretty good, easy to follow documentation. There is a good "getting started" guide if you want to give them a try.
Spring Boot - Spring Boot is a bootstrapping framework that makes getting up and running with applications much easier. There are auto configurations made for you but give you options to change them. You can use Spring MVC or Jersey as the REST framework when you work with Spring Boot. There is also an Initializer, which is similar to the Maven Archetypes, but is interactive and allows you to add what you want to your applications.
So I just accepted the fact that java is an utter mess and pulled these dependencies in:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.4.2</version>
</dependency>
So i can launch everything like so:
public static void main(String[] args) throws Exception {
SomeDependency dependency = new SomeDependency();
Users usersEndpoint = new Users(dependency);
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
serverFactory.setResourceClasses(usersEndpoint.getClass());
serverFactory.setResourceProvider(usersEndpoint.getClass(), new SingletonResourceProvider(usersEndpoint));
serverFactory.setAddress("http://localhost:8080/");
serverFactory.create().start();
}
I have more endpoints, of course, so I put everything in a for loop.
As to why I'm answering from a guest account, the account I used to ask this question was created using a disposable email address, and I can no longer access it.
Hope this helps anyone in the future!

How to activate JMX monitoring in spring boot standalone app

I went via almost all docs and all but not able to get grip on this mysterious stuff.
so my question - Can I use my standalone spring boot app to monitor health and other metrics of my app via http jmx url? Do I need to configure something else for this?
I have added below dependency in boot app.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
I have also configured below properties in my config file.
management.endpoints.web.exposure.include=*
management.endpoints.jmx.unique-names=true
management.server.port=8080
management.server.ssl.enabled=false
When I try to hit URL : http://localhost:8080/actuator/jolokia/health I am not able to see any results.
Also tried adding custom end point like below but not working.
#Endpoint(id="mypoint")
#Component
public class myPointEndPoint {
#ReadOperation
public String mypoint(){
return "Hello" ;
}
with additional property
management.endpoint.mypoint.enabled=true
The problem is the url you are trying to invoke.
First, retrieve the possible mbeans with: http://localhost:8080/actuator/jolokia/list
When you take a look at the Health mbean, you must provide the unique name and the operation (op).
In my case, it looked like: http://localhost:8080/actuator/jolokia/exec/org.springframework.boot:type=Endpoint,name=Health,identity=4200098/health
Also check the Jolokia documentation: https://jolokia.org/reference/html/index.html

spring boot wants me to start server because I have servlet on classpath

today I faced following problem:
One of my core dependencies unfortunately pulls servlet.api to my classpath. Because of this, my spring-boot thinks I'm automatically a server, while I'm a desktop app, and does not want to start without some factories needed to web.
This is what it says:
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
Unfortunately I can't get rid of this dependency, and for them to fix their transitive dependencies will probably take some time.
Is there any hack to walk around this?
Thanks
If you're using Spring boot 2.x, you can disable the web application by setting the spring.main.web-application-type property to none:
spring.main.web-application-type=none
If you're using Spring boot 1.x, you could set the spring.main.web-environment property:
spring.main.web-environment=false
The reason this changed is because Spring boot 2.x can now be configured to be either reactive, servlet-based or none, while in Spring boot 1.x it was either servlet-based or none (so it could be just a boolean).
Alternatively, you can also use a custom SpringApplication instance as mentioned by the documentation (and in the comments):
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE) // Use this for Spring boot 2.x
.web(false) // Use this for Spring boot 1.x
.run(args);
}
Not all Spring applications have to be web applications (or web services). If you want to execute some code in a main method but also bootstrap a Spring application to set up the infrastructure to use, you can use the SpringApplication features of Spring Boot. A SpringApplication changes its ApplicationContext class, depending on whether it thinks it needs a web application or not. The first thing you can do to help it is to leave server-related dependencies (e.g. servlet API) off the classpath. If you cannot do that (for example, you run two applications from the same code base) then you can explicitly call setWebApplicationType(WebApplicationType.NONE) on your SpringApplication instance or set the applicationContextClass property (through the Java API or with external properties).
You are missing some required JAR file to run it as web application. Please make sure to that you have included spring-boot-starter-web.jar file.
If we are using maven, do it as follows.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.3.2.RELEASE</version>
</dependency>

Don't spring-boot-starter-web and spring-boot-starter-webflux work together?

When I start to learn the spring-webflux, I have the question about this component.
I built a simple project, using maven to manage it. I addded the dependencies related to spring-boot-starter-web and spring-boot-starter-webflux, like :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
But it doesn't work. When removing the spring-boot-starter-web dependency, it can work well.
As explained in the Spring Boot reference documentation section about the web environment, adding both web and webflux starters will configure a Spring MVC web application.
This is behaving like that, because many existing Spring Boot web applications (using MVC) will depend on the webflux starter to use the WebClient. Spring MVC partially support reactive return types, so this is an expected use case. The opposite isn't really true, since a reactive application is not really likely to use Spring MVC bits.
So using both web and webflux starters is supported, but it will configure a Spring MVC application. You can always force the Spring Boot application to be reactive with:
SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)
But it's still a good idea to clean up dependencies as it would be easy to use a blocking feature in your reactive web application.
I had a similar issue using spring-boot-starter-webflux and spring-data-geode causing
DEBUG [http-nio-8082-exec-2] org.sprin.web.servl.resou.ResourceHttpRequestHandler 454 handleRequest: Resource not found
It was resolved by changing the application type
#SpringBootApplication
public class Web {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Web.class);
app.setWebApplicationType(WebApplicationType.REACTIVE);
SpringApplication.run(Web.class, args);
}
}
The whole class looks like this
After setting the application type, if I don't then call the SpringApplication in a static way, I get this:

How to prevent auto start of tomcat/jetty in Spring Boot when I only want to use RestTemplate

I want to use RestTemplate/TestRestTemplate by including the artifact in a SpringBoot application
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
But this automatically starts Tomcat or Jetty. Is there a way to turn it off, or by not including the above artifact. TestRestTemplate is in the boot artifact, but not the base RestTemplate.
Spring Boot is not going to start a web container if it's not present. spring-web does not provide any embedded container. You may want to analyse the dependencies of your project (try mvn dependency:tree).
If you want to make sure a web server is not started in your spring boot application, you can set the following configuration key
spring.main.web-application-type=none
Or you can use the SpringApplicationBuilder
new SpringApplicationBuilder(YourApp.class)
.web(WebApplicationType.NONE).run(args);
Since Spring Boot 2.0.0 this property is deprecated and following is the new way:
spring.main.web-application-type=none
This change is because Spring Boot the support for reactive server.
You can just close the app according to https://spring.io/guides/gs/async-method/. Although this still stars Tomcat, but will stop the app at the end without keeping the tread running.
SpringApplication.run(MyApp.class, args).close();

Categories

Resources