Spring Boot 2.0.0.M2 and Spring Data Elasticsearch configuration - spring

I'm trying to move my project to Spring Boot 2.0.0.M2.
This is my old Spring Data Elasticsearch configuration:
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
#Profile("production")
#Configuration
#EnableElasticsearchRepositories(basePackages = "com.example.domain.repository.elasticsearch")
public class ElasticsearchConfig {
#Value("${elasticsearch.host}")
private String host;
#Value("${elasticsearch.port}")
private int port;
#Bean
public Client client() throws Exception {
return TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(host), port));
}
#Bean
public ElasticsearchOperations elasticsearchTemplate() throws Exception {
return new ElasticsearchTemplate(client());
}
}
Right now I faced a following issue:
1. The method builder() is undefined for the type TransportClient
2. InetSocketTransportAddress cannot be resolved to a type
On the Maven classpath I have Spring Data Elasticsearch 3.0.0.M4:
How to properly configure current version of Spring Data Elasticsearch ?
UPDATED
For my tests I use Embedded Elasticsearch with a following application.properties:
#Elasticsearch
spring.data.elasticsearch.properties.http.enabled=true
spring.data.elasticsearch.properties.http.port=9250
spring.data.elasticsearch.properties.path.home=target/test-elasticsearch-db
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=60s
This is my ES test config:
#Profile("test")
#Configuration
#EnableElasticsearchRepositories(basePackages = "com.example.domain.repository.elasticsearch")
public class ElasticsearchTestConfig {
}
Right now the test fails with a following error:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'elasticsearchTemplate' defined in class path resource [org/springframework/boot/autoconfigure/data/elasticsearch/ElasticsearchDataAutoConfiguration.class]: Unsatisfied dependency expressed through method 'elasticsearchTemplate' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.elasticsearch.client.Client' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:726) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:458) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
and
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.elasticsearch.client.Client' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1478) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1089) ~[spring-beans-5.0.0.RC2.jar:5.0.0.RC2]
What is wrong here and how to fix this ?

Spring Boot 2.0 uses Elasticsearch 5 which includes some breaking API changes. You would be shielded from those changes if you used Spring Boot's auto-configuration rather than trying to write your own.
All that's needed is a value for the spring.data.elasticsearch.cluster-nodes property. With that in place, Spring Boot will auto-configure both a TransportClient and an ElasticsearchTemplate.

Had similar issue when migrating from spring-boot 1.5.6 to 2.0.0. The reason seems to be previous support for embedded elasticsearch is not more supported and the same is reflected in spring-boot.
Previously leaving the following property empty
spring.data.elasticsearch.cluster-nodes
put the following into use
spring.data.elasticsearch.properties.path.home
and spring-boot created given directory in the target folder for embedded mode to run. With spring-boot 2.0.0 (spring-boot-autoconfigure-2.0.0.RELEASE.jar to be precise) cluster-nodes has become asserted for non-null value causing elasticsearchTemplate bean not to be created under the hood.
That is the reason why your app stop working so suddenly.

Related

JUnit #DataJpaTest org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name

I'm trying to do a test on the repository using #DataJPATest but #Autowired is not working
I already tried looking for examples of junit5 with #DataJpaTest, but I didn't find it
I tried adding other dependencies, I used #SpringTest and it worked, but I wanted to use #DataJpaTest
package com.projetoSpring.catalog.repositories;
import com.projetoSpring.catalog.model.Product;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import java.util.Optional;
#DataJpaTest
public class ProductRepositoryTests {
#Autowired
private ProductRepository repositorys;
#Test
public void deleteShouldDeleteObjectWhenIdExists() {
long exintingId = 1L;
repositorys.deleteById(exintingId);
Optional<Product> result = repositorys.findById(1L);
Assertions.assertFalse(result.isPresent());
}
}
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.projetoSpring.catalog.repositories.ProductRepositoryTests': Unsatisfied dependency expressed through field 'repositorys'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.projetoSpring.catalog.repositories.ProductRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I managed to solve it, I updated spring to version 2.7.5 and I verified that the test dependency was not downloading correctly, I redid the pom.xml and it worked.

#ConditionalOnBean not work for spring boot test

Hy everyone! I'm trying to solve the problem for a very long time.
There is very simple spring boot test
public class ApplicationTest {
#Test
void testContext() {
SpringApplication.run(Application.class);
}
}
And several beans...
#Service
#ConditionalOnBean(CommonService.class)
#RequiredArgsConstructor
public class SimpleHelper {
...
#Service
#RequiredArgsConstructor
#ConditionalOnBean(CommonFeignClient.class)
public class CommonService {
...
#FeignClient(
name = "CommonClient",
url = "localhost:8080"
)
public interface CommonFeignClient {
And the main class look as
#SpringBootApplication
#EnableFeignClients(clients = AnotherFeignClient.class)
public class Application {
When the spring application starts everything works ok. SimpleHelper does not created.
But in the spring boot test throw the exception:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'simpleHelper' defined in URL [...]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'foo.bar.CommonService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Please help, I don't understand what's going on anymore =)
Spring boot version is 2.3.0.RELEASE.
To use #ConditionalOnBean correctly, CommonService needs to be an auto-configuration class or defined as a bean by an auto-configured class rather than a service that's found by component scanning. This ensures that the bean on which CommonService is conditional has been defined before the condition is evaluated. The need for this is described in the annotation's javadoc:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.

Why #MockBean ConversionService causes an error

Given: JUnit 5 and empty SpringBoot project created with Intellij IDEA Ultimate. I get the same results for projects with a controller that contains #Autowired ConversionService.
I need to use in a test the mocked version of ConversionService.
This is my approach:
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.convert.ConversionService;
#SpringBootTest
public class ConversionServiceTest {
#MockBean
ConversionService conversionService;
#Test
void test() {}
}
The above code causes the error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.format.support.FormattingConversionService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value="mvcConversionService")}
What is the reason for this error and what is the solution?
The solution is to replace #MockBean by #SpyBean.

Spring #Service defined in Kotlin file results in 'UnsatisfiedDependencyException: Error creating bean' when referenced

I have an existing Springboot/Java application and I wanted to start migrating the application to Kotlin. I create a #Service in Kotlin, and my unit test fails at runtime because Spring can't find my service.
My unit test:
#ExtendWith(SpringExtension::class)
#SpringBootTest(classes = arrayOf(Application::class))
class Junit5SpringKotlinTests {
#Autowired // Springboot ConfigurationProperties (application.yaml) mapped to Java bean. This works.
lateinit var applicationConfigurationJavaBean: EnvironmentProperties
fun appProperties(): EnvironmentProperties.Environment? {
return applicationConfigurationJavaBean.environments[applicationConfigurationJavaBean.activeEnvironment]
}
#Autowired
lateinit var testServiceKotlinBean :TestService // My Kotlin #Service which can not be found at runtime.
#Test
fun `ApplicationConfiguration`() {
println(appProperties()?.baseClientId)
testServiceKotlinBean.hello()
}
}
My Kotlin service: TestService.kt
import org.springframework.stereotype.Service
#Service
class TestService {
fun hello() {
println("hello from service")
}
}
My error at runtime executing the unit test:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'Junit5SpringKotlinTests': Unsatisfied dependency expressed through field 'testServiceKotlinBean'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'api.model.TestService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'api.model.TestService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1509)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
FWW, I am able to write JUnit5 SpringBootTests in Kotlin test code and exercise all my Java components, so this has something to do with Kotlin components being discovered at runtime by Spring. Java components work.
I suspected this might be a build problem and have seen similar issues. I have the following included in my pom.xml:
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin.version}</version>
</dependency>
so that wasn't the problem.
The problem was the package name I used for the Kotlin service. Kotlin is nice in that I don't have put my files in the same package path like java. But my Spring classpath scanning was relying on a package: A.B.C and below and my Kotlin path only had C. I changed the package name in my Kotlin file to be A.B.C.TestService and all is fine. Yay.

Spring JPA not implementing/autowiring repository despite #EnableJpaRepositories annotation

I'm getting an exception when I start my application, where Spring complain about UnsatisfiedDependencyException:
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'applicationConfig': Unsatisfied dependency expressed through field 'controlRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [com.oak.api.finance.repository.ControlRepository]: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
My application is organized in this format:
I declared my repository interfaces, with the proper Spring JPA annotations:
#RepositoryRestResource(collectionResourceRel = "exchange", path = "exchanges")
public interface ControlRepository extends PagingAndSortingRepository<Control, Long> {
}
I annotated the EntryPoint class that contains the main method
#SpringBootApplication
#EntityScan(basePackages = {"com.oak.api.finance.model.dto"})
#EnableJpaRepositories(basePackages = {"com.oak.api.finance.repository"})
public class EntryPoint {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(EntryPoint.class);
logger.info("Starting application");
ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// SpringApplication.run(EntryPoint.class, args);
ctx.getBean(ApplicationServer.class).start();
}
I used #Autowired to inject my repository into my spring config (java based) ApplicationConfig class:
#Autowired
private ControlRepository controlRepository;
#Autowired
private CompanyRepository companyRepository;
#Autowired
private SectorRepository sectorRepository;
Essentially I want to control the dependency on Spring and limit it to a couple of packages, (the repositories, the java config, and the program entry point - EntryPoint)
I assumed that, by specifying #EnableJpaRepositories with the package where my repositories are located, spring would create a proxy for my repository and instantiate an instance of that, and that by the time I call :
ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class)
The repositories instances would be present in the beans poole and would be possible to autowire them into my ApplicationConfig context, and then inject them into my controller.
This is clearly not happening, and Spring is complaining about the missing Bean to autowire, but I'm not sure what am I missing.
Below a snapshot of my packages:
any ideas?
My guess is your repositories are not being scanned, so as a result beans are not getting created. Can you try removing these 2 annotations
#EntityScan(basePackages = {"com.oak.api.finance.model.dto"})
#EnableJpaRepositories(basePackages = {"com.oak.api.finance.repository"})
And keep only #SpringBootApplication. If this is not working, you might need to check the package structure (if possible paste a screenshot here)
Edit 1
replace #SpringBootApplication with
#Configuration
#EnableAutoConfiguration
#ComponentScan("com.oak")
Edit2
Use
new SpringApplicationBuilder()
.sources(SpringBootApp.class)
.web(false)
.run(args);
Or use CommandLineRunner after changing ComponentScan path to "com.oak" as mh-dev suggested

Resources