"spring-boot-starter-web" compatibility with Quarkus - spring-boot

I'm new to Quarkus and I'm validating the migration viability of a quite complex Spring Boot application.
For now, I'm trying to make the first step into Quarkus world, which is to get the application running on Quarkus JVM (non native option) with as little changes as possible.
After dealing with "spring-orm", "spring-jdbc" and "spring-web" (client part) dependencies not covered by Quarkus spring extensions, now I'm facing the Quarkus build error below:
[ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:2.2.1.Final:build (default) on project rules-engine-core: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[ERROR] [error]: Build step io.quarkus.arc.deployment.ArcProcessor#registerBeans threw an exception: java.lang.IllegalArgumentException: Producer method return type not found in index: org.springframework.boot.web.servlet.ServletRegistrationBean
After some research, I'm still lost about the truly right way to solve this problem that seems to be caused by code below:
import javax.ws.rs.core.Application;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.catalina.valves.StuckThreadDetectionValve;
import org.springframework.beans.factory.annotation.Autowired;
#Configuration
#SpringBootApplication( exclude = TransactionAutoConfiguration.class )
public class WebApplicationConfiguration extends SpringBootServletInitializer {
#Autowired
private [ApplicationName]ExecutorSemaphoreValve semaphoreValve;
// Replaces the need for web.xml
#Bean
public ServletRegistrationBean servletRegistrationBean( ApplicationContext context ) {
return new ServletRegistrationBean( new CXFServlet(), "/*" );
}
// Configure the embedded tomcat to use same settings as default standalone
// tomcat deploy
#Bean
public AbstractServletWebServerFactory embeddedServletContainerFactory() {
// Made to match the context path when deploying to standalone tomcat-
// can easily be kept in sync w/ properties
TomcatServletWebServerFactory factory =
new TomcatServletWebServerFactory( CorePropertyUtils.engineContext(), CorePropertyUtils.enginePort() );
StuckThreadDetectionValve stuckThreadValve = new StuckThreadDetectionValve();
semaphoreValve.setBlock( false );
semaphoreValve.setFairness( false );
factory.addContextValves( stuckThreadValve, semaphoreValve );
return factory;
}
// Used when deploying to a standalone servlet container, i.e. tomcat
#Override
protected SpringApplicationBuilder configure( SpringApplicationBuilder application ) {
return application.sources( Application.class );
}
}
I've found a similar post here, but it was result of lack of Quarkus spring extensions addition, which I believe is not my case.
It's just a matter of jandex indexation?
Do I need to change the code that uses the 'spring-boot-web' dependencies? (I hope not, at least for first migration stage)
Thanks a lot and best regards!

Related

Spring loading and using bean from module

I have divided my project into 3 modules- client, server, and core. In the core module, I defined the required settings for the sentry. I would like to use those beans in client and server modules so that I don't have to redefine them for each module.
Is it possible? If so please give example.
My current sentry setup in the core module.
org/project/core/configurations/SentryConfiguration.java
package org.project.core.configurations;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
#Configuration
public class SentryConfiguration {
/*
This class is constructed according official sentry documentation.
URL: https://docs.sentry.io/platforms/java/guides/spring/
*/
#Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new io.sentry.spring.SentryExceptionResolver();
}
#Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new io.sentry.spring.SentryServletContextInitializer();
}
}

How to close spring boot with routing properly

I'm trying to use a routing to stop my spring boot application with the code below
with
#GetMapping("/close/")
fun terminate() {
exitProcess(0)
}
but the test server has a different API, so I can't use this code (It's shutdown a whole system)
My question is: how to stop only my spring boot application (replace exitProcess(0))
You can do it using Actuator's shutdown for example.
Find an example here.
Or you can just close your Application Context and that will work.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class SomeRestController {
#Autowired
ApplicationContext applicationContext;
#GetMapping("/close")
public void terminate() {
((AbstractApplicationContext)applicationContext).close();
}
}

Error Messages as Key Value Pairs - from a Properties File in classpath - Spring boot 2.0

We are currently on a Spring Boot Version 1.x
We have Error Messages (Error Key -> Error Code) pairs in our error.properties file (this is in the class path).
We leveraged PropertiesConfigurationFactory to get these Error Key and Error Code pairs in to a POJO, this POJO had a Map
Hence very convenient to be used across our application to get an Error code for a given Error Key.
What is its equivalent in Spring Boot 2.x ?.
Assuming you have error.properties file with the below contents:
errors.error1=101
errors.error2=102
errors.error3=103
A simple spring boot app that demonstrates the injection of these properties :
package snmaddula.remittance;
import java.util.Map;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
#SpringBootApplication
#ConfigurationProperties
#PropertySource("classpath:error.properties")
public class DemoApplication {
private Map<String, Integer> errors;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner cli() {
return (args) -> {
System.out.println(errors); // you can print and see the error properties injected to this map.
};
}
public void setErrors(Map<String, Integer> errors) {
this.errors = errors;
}
}
With the use of #PropertySource and #ConfigurationProperties we can enable property injection provided we have a setter method for our attribute.
When you run this program, you can see the properties getting printed on to the console as I added a CommandLineRunner cli() {..} to show the working of it.
The working sample is available on GitHub.

NoUniqueBeanDefinitionException with #EnableExperimentalNeo4jRepositories annotation and SpringBoot 1.4.2

I'm having an issue with Spring boot 1.4.2.M1 and #EnableExperimentalNeo4jRepositories.
It seems to be a conflict between two beans, one spring boot, one spring-data-neo4j.
Here is a stack trace excerpt:
18:12:15.891 [main] DEBUG o.s.b.d.LoggingFailureAnalysisReporter - Application failed to start due to an exception
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.neo4j.ogm.session.Session' available: expected single matching bean but found 2: getSession,org.springframework.data.neo4j.transaction.SharedSessionCreator#0
And another...
Parameter 0 of method setSession in org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean required a single bean, but 2 were found:
- getSession: defined in BeanDefinition defined in class path resource [org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration$SpringBootNeo4jConfiguration.class]
- org.springframework.data.neo4j.transaction.SharedSessionCreator#0: defined by method 'createSharedSession' in null
Anybody have any idea how to solve this?
Below is my Neo4j Configuration
package com.domain.core.context;
import javax.annotation.PostConstruct;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.session.event.Event;
import org.neo4j.ogm.session.event.EventListenerAdapter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.repository.config.EnableExperimentalNeo4jRepositories;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#Configuration
#ComponentScan("com.domain")
#EnableExperimentalNeo4jRepositories(basePackages = "com.domain.core.repository")
#EnableTransactionManagement
#SpringBootApplication(exclude = Neo4jDataAutoConfiguration.class)
public class TestPersistenceContext {
#PostConstruct
public void init() {
log.info("TheScene.Co: Initializing Test Neo4jConfig ...");
}
#Bean
public Neo4jTransactionManager transactionManager() throws Exception {
return new Neo4jTransactionManager(sessionFactory());
}
#Bean
public SessionFactory sessionFactory() {
return new SessionFactory(getConfiguration(), "com.domain") {
#Override
public Session openSession() {
Session session = super.openSession();
session.register(new EventListenerAdapter() {
#Override
public void onPreSave(Event event) {
// do something - like set an id on an object
log.debug("***** Saving domain object ********");
}
});
return session;
}
};
}
#Bean
public org.neo4j.ogm.config.Configuration getConfiguration() {
org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
config.driverConfiguration().setCredentials("neo4j", "password")
.setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver");
return config;
}
}
You must be using Spring Data Neo4j (SDN) version 4.2.0.M1. This milestone release was put out to get feedback on several big changes from 4.1.x.
SDN 4.2.0.RC1 should be out later this week but for now 4.2.0.BUILD-SNAPSHOT is actually quite stable in the lead up to Ingalls release train for Spring Data in Decemeber.
I have written a guide for users coming from SDN 4.0/4.1 which goes over how to upgrade to the snapshot build.
In this guide there is a link to an example project branch which shows how to get this version to work with Spring Boot 1.4.x with a few minor work arounds.
WIth the upcoming release of Spring Boot 1.5, we have updated all the autoconfiguration to work straight out of the box with SDN 4.2. We will update the documenation for Spring Boot closer to release.

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