Why restTemplateBuilder is not loaded in Spring Boot 2.2.4? - spring

I am trying to upgrade SpringBoot from 2.1.1 to 2.2.4.RELEASE.
I found an issue I can't solve.
When I try to run integration tests I run into an error:
Bean method 'restTemplateBuilder' in 'RestTemplateAutoConfiguration' not loaded because NoneNestedConditions 1 matched 0 did not; NestedCondition on RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition.ReactiveWebApplication found ReactiveWebApplicationContext
This is my class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RoutesIT {
I noticed that a new #Conditional(NotReactiveWebApplicationCondition.class) is added to RestTemplateAutoConfiguration.class and this is probably the reason why restTemplateBuilder is not loaded properly.
I can create this bean manually but I don't think that's the best solution.
What should I do to make it work again?

Actually you should use WebClient instead of RestTemplate when you are reactive. See documentation here:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-client
But if you have other reasons to keep on using RestTemplate add this class into your test package:
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class TestConfiguration {
#Bean
public RestTemplateBuilder restTemplateBuilder() {
// Need to provide a rest template builder because
// #RestTemplateAutoConfiguration does not work with webflux
return new RestTemplateBuilder();
}
}

Related

Ehcache - Cannot find cache name for Builder

I've looked through a lot of similar questions asked here but I'm still not able to find a solution so here's my issue:
I'm trying to setup Ehcache on springboot.
Spring 2.2.6.RELEASE
Ehcache 3.8.1
CacheService
I've got a cache named `myCache`.
#Cacheable(value = "myCache")
#GetMapping("/get")
public String get();
CacheConfig
And my config
#Configuration
#EnableCaching
public class CacheConfig {
public CacheConfig() {
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(SimpleKey.class, String.class, ResourcePoolsBuilder.heap(10))).build();
cacheManager.init();
}
}
Error
But I'm getting the following error:
java.lang.IllegalArgumentException: Cannot find cache named 'myCache' for Builder...
I managed to get it to work if I put the config in the xml file, but I rather have it in java.
#Cacheable(value = "myCache") doesn't create a cache named myCache in Ehcache. At runtime, if a cache named myCache is available in Ehcache, it'll use that cache for caching. If not, when attempting to cache at runtime, the exception java.lang.IllegalArgumentException: Cannot find cache named 'myCache' will be thrown. For #Cacheable(value = "myCache") to work with Ehcache as the backend, the cache needs to be created somewhere and Spring needs to be made aware of that cache. The simplest way to do that is to include the spring-boot-starter-cache dependency, add an ehcache.xml with the Ehcache config to classpath and set the config spring.cache.jcache.config: classpath:ehcache.xml in application.yml. You can find a sample application that does that on github
Instead if you do want to configure Ehcache programmatically, you need a org.springframework.cache.CacheManager bean, to initialize the Ehcache config and link it to Spring. The bean definition could look something like below:
import javax.cache.Caching;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public CacheManager ehCacheManager() {
CacheConfiguration<SimpleKey, String> cacheConfig = CacheConfigurationBuilder
.newCacheConfigurationBuilder(SimpleKey.class, String.class, ResourcePoolsBuilder.heap(10))
.build();
javax.cache.CacheManager cacheManager = Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider")
.getCacheManager();
String cacheName = "myCache";
cacheManager.destroyCache(cacheName);
cacheManager.createCache(cacheName, Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfig));
return new JCacheCacheManager(cacheManager);
}
}
Sample working application that configures Ehcache for Spring through code can be found here on github.

Could not autowire JobLauncherTestUtils

I am attempting to test a simple spring batch application.
Using the Spring Batch documentation as a guide (found here), I have created the following test class:
import org.junit.runner.RunWith;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.jupiter.api.Assertions.assertNotNull;
#SpringBatchTest
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = BatchConfig.class)
class BatchConfigTest {
#Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
#Test
void userStep() {
assertNotNull(jobLauncherTestUtils, "jobLauncherTestUtils should not be null");
}
}
According to docs #SpringBatchTest should inject the JobLaucherTestUtils bean. However, when I run the test, the assertion fails. I have also tried defining the bean in an inner configuration class and had the same result:
static class TestConfiguration {
#Autowired
#Qualifier("userJob")
private Job userJob;
#Bean
public JobLauncherTestUtils jobLauncherTestUtils() {
JobLauncherTestUtils utils = new JobLauncherTestUtils();
utils.setJob(userJob);
return utils;
}
}
Is there something I'm missing? The full source code can be found here.
I am using Spring Batch v4.2.0 and JUnit 5
You are using #RunWith(SpringRunner.class) which is for JUnit 4. You need to use #ExtendWith(SpringExtension.class) for JUnit 5 tests:
#SpringBatchTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = BatchConfig.class)
class BatchConfigTest {
// ...
}
I had a same problem with Spring Batch 4.1.3 and JUnit 4.12.
Replacing #SpringBatchTest with #SpringBootTest solved the problem.
I was seeing the same error in IntelliJ and spent ages looking into why before I ran the test. It was being injected just fine, IntelliJ was incorrectly telling me the bean wasn't there.
:facepalm:
Posting this in case someone faces the same issue.

#SpyBean not working with Pact and JUnit 5

I'm trying to use the #SpyBean to mock a method of a #Component and doesn't work. #MockBean works (followed the example). I've tried, read and researched many ways but couldn't make it work.
Here's the example:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment. DEFINED_PORT)
#ExtendWith(SpringExtension::class)
#Provider("MyMicroService")
#PactFolder("../../../pacts")
internal class ClientContracts {
#SpyBean
private lateinit var myService: MyService
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider::class)
fun pactVerificationTestTemplate(context: PactVerificationContext) {
context.verifyInteraction()
}
#State("default", "NO_DATA")
fun toDefaultState() {
reset(processService)
}
}
(I super simplified the test function so it's easier to read, I'd be actually doing doReturn(...).when(...).blah())
I'm always getting the "not a mock" error, because the object is always the bean wrapped by Spring CGLIB:
org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class com.blah.MyServiceImpl$$EnhancerBySpringCGLIB$$9712a2a5
at com.nhaarman.mockitokotlin2.MockitoKt.reset(Mockito.kt:36)
...
I've tried:
with #SpringJUnitConfig
with a separate #TestConfiguration, but got resolved to same above bean
Using Mockito.initAnnotations(this) in a #BeforeEach
and more, I've tried with so many combinations that I can't remember...
Is there something that I'm missing? Or an option that I don't know?
Above issue is not related to the pact or pact JVM library
The issue is not about spring
Spring - I use spring with mockito and it works, the simple example is:
import com.nhaarman.mockito_kotlin.doReturn
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.SpyBean
import org.springframework.test.context.junit.jupiter.SpringExtension
#ExtendWith(value = [SpringExtension::class])
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = [Application::class]
)
internal class processorIntegrationTest : IntegrationTest() {
#SpyBean
// #MockBean
private lateinit var processor: Processor;
#Test
internal fun abcd() {
doReturn("something").`when`(processor).get()
val get = processor.get()
assertThat(get).isEqualTo("something")
}
}
Mockito - mockito_kotlin or mockito extension works with SpyBean
Issue is about mockito + CGLIB
CGLIB - from your logs feels like class com.blah.MyServiceImpl$$EnhancerBySpringCGLIB$$9712a2a5 there is a wrapper on top of your service implementation which is SpyBean.
Which means CGLIB wrapper is not and the error is for that.
Try removing CGLIB wrapper and it will work

Using Spring boot, I am unable to refer a class present in another jar

I am trying to build a spring boot web application. I want to refer a class from another jar. The class name is SalaryHandler.
I have done the following configuration in the class having
#SpringBootApplication annotation:
#Bean
public SalaryHandler iSalary() {
return new SalaryHandler();
}
In the class, where it is required, I have used autowiring annotation like this:
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.salary.SalaryHandler;
//#Service - not working
//#Component - not working
public class SalaryDelegatorImpl implements SalaryDelegator {
#Autowired
private SalaryHandler iSalary;
#Override
public void show() {
iSalary.testSalary();
}
}
The code is compiling fine, but when this iSalary object is used to call its method, nullpointer exception is thrown.
Just to note that SalaryHandler is present inside another jar and is not using any spring annotation, its code is as below:
package com.salary;
public class SalaryHandler implements ISalary {
public void testSalary() {
System.out.println("Salary test successful...");
}
}
you need to attempt Autowire with #Component. In order to get this to work, you'll have to annotate a method in your #Configuration class. Something like this should allow you to autowire the class:
#Configuration
#ComponentScan("com.package.where.my.class.is")
public class ConfigClass{
#Bean
public JPADataService jpaDataService(){
return new JPADataService();
}
}
I am able to fix this. The problem was somewhere inside code, I was calling SalaryDelegatorImpl using new operator(from inside a factory class), so that was not being managed by Spring. As a result, the #Autowired on SalaryHandler, was not working.
I changed my factory to be spring managed, and then it worked fine.
Thanks everyone for the support.

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.

Resources