Spring beans in configuration file form a cycle (Kotlin, Redis) - spring

I'm new to Kotlin and working on a Spring application, trying to set Redis configuration. I keep getting this problem:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'redisConfig': Requested bean is
currently in creation: Is there an unresolvable circular reference?
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| redisConfig defined in file [file]
└─────┘
I don't understand what exactly causes this problem and how to fix it. It seems to me that RedisConfig is being created inside RedisConfig, but I'm not sure and don't understand where this problem comes from.
Here's RedisConfig.kt
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.MessageListener
import org.springframework.data.redis.connection.RedisStandaloneConfiguration
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.listener.ChannelTopic
import org.springframework.data.redis.listener.RedisMessageListenerContainer
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
#Configuration
class RedisConfig(val messageListener: MessageListener) {
#Value("\${spring.redis.host}")
lateinit var redisHost: String
#Value("\${spring.redis.port}")
lateinit var redisPort: String
#Value("\${spring.redis.topic}")
lateinit var redisTopic: String
#Bean
fun jedisConnectionFactory(): JedisConnectionFactory {
val config = RedisStandaloneConfiguration(redisHost, redisPort.toInt())
val jedisClientConfiguration = JedisClientConfiguration.builder().usePooling().build()
val factory = JedisConnectionFactory(config, jedisClientConfiguration)
factory.afterPropertiesSet()
return factory
}
#Bean
fun redisTemplate(): RedisTemplate<String, Any> {
val template: RedisTemplate<String, Any> = RedisTemplate()
template.connectionFactory = JedisConnectionFactory()
template.valueSerializer = GenericJackson2JsonRedisSerializer()
return template
}
#Bean
fun topic(): ChannelTopic = ChannelTopic(redisTopic)
#Bean
fun newMessageListener(): MessageListenerAdapter = MessageListenerAdapter(messageListener)
#Bean
fun redisContainer(): RedisMessageListenerContainer {
val container = RedisMessageListenerContainer()
container.connectionFactory = jedisConnectionFactory()
container.addMessageListener(newMessageListener(), topic())
return container
}
}

I believe you should use your method jedisConnectionFactory() instead of a new JedisConnectionFactory class, in redisTemplate method.
Basicly, you should do the same as you do in redisContainer()

Related

Spring Boot testing: Cannot bind #ConfigurationProperties - Ensure that #ConstructorBinding has not been applied

In a Spring Boot unit test, how can you mock #ConstructorBinding #ConfigurationProperties data class?
Setup
Both
Kotlin 1.4.30 (for unit tests and config classes)
Java 15 (with --enable-preview) (for business logic)
Spring Boot 2.4.2
Junit 5.7.1
Mockito (mockito-inline) 3.7.7
Maven 3.6.3_1
I want to test FtpService (a #Service, which has a RestTemplate) with different configurations.
Properties for the FtpService come from a Kotlin data class - UrlProperties - which is annotated with ConstructorBinding and #ConfigurationProperties.
Note: FtpService's constructor extracts a property from UrlProperties. This means that UrlProperties must be both mocked and stubbed before Spring loads FtpService
Error
When I try and mock UrlProperties so that I can set the properties for different tests, I either receive an error, or am unable to insert the bean
Cannot bind #ConfigurationProperties for bean 'urlProperties'. Ensure that #ConstructorBinding has not been applied to regular bean
Code
`#SpringBootTest` of FtpService | `src/test/kotlin/com/example/FtpServiceTest.kt`
import com.example.service.FtpService
import com.example.service.UrlProperties
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.context.annotation.Bean
import org.springframework.test.context.ContextConfiguration
#TestConfiguration
#SpringBootTest(classes = [FtpService::class])
#AutoConfigureWebClient(registerRestTemplate = true)
class FtpServiceTest
#Autowired constructor(
private val ftpService: FtpService
) {
// MockBean inserted into Spring Context too late,
// FtpService constructor throws NPE
// #MockBean
// lateinit var urlProperties: UrlProperties
#ContextConfiguration
class MyTestContext {
// error -
// > Cannot bind #ConfigurationProperties for bean 'urlProperties'.
// > Ensure that #ConstructorBinding has not been applied to regular bean
var urlProperties: UrlProperties = mock(UrlProperties::class.java)
#Bean
fun urlProperties() = urlProperties
// error -
// > Cannot bind #ConfigurationProperties for bean 'urlProperties'.
// > Ensure that #ConstructorBinding has not been applied to regular bean
// #Bean
// fun urlProperties(): UrlProperties {
// return UrlProperties(
// UrlProperties.FtpProperties(
// url = "ftp://localhost:21"
// ))
// }
}
#Test
fun `test fetch file root`() {
`when`(MyTestContext().urlProperties.ftp)
.thenReturn(UrlProperties.FtpProperties(
url = "ftp://localhost:21"
))
assertEquals("I'm fetching a file from ftp://localhost:21!",
ftpService.fetchFile())
}
#Test
fun `test fetch file folder`() {
`when`(MyTestContext().urlProperties.ftp)
.thenReturn(UrlProperties.FtpProperties(
url = "ftp://localhost:21/user/folder"
))
assertEquals("I'm fetching a file from ftp://localhost:21/user/folder!",
ftpService.fetchFile())
}
}
Workaround - manual definition every test
The only 'workaround' is to manually define all beans (which means I miss out on Spring Boot magic during testing) and in my view is more confusing.
Workaround - manual redefinition each test | `src/test/kotlin/com/example/FtpServiceTest2.kt`
import com.example.service.FtpService
import com.example.service.UrlProperties
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.springframework.boot.web.client.RestTemplateBuilder
class FtpServiceTest2 {
private val restTemplate =
RestTemplateBuilder()
.build()
private lateinit var ftpService: FtpService
private lateinit var urlProperties: UrlProperties
#BeforeEach
fun beforeEachTest() {
urlProperties = mock(UrlProperties::class.java)
`when`(urlProperties.ftp)
.thenReturn(UrlProperties.FtpProperties(
url = "default"
))
ftpService = FtpService(restTemplate, urlProperties)
}
/** this is the only test that allows me to redefine 'url' */
#Test
fun `test fetch file folder - redefine`() {
urlProperties = mock(UrlProperties::class.java)
`when`(urlProperties.ftp)
.thenReturn(UrlProperties.FtpProperties(
url = "ftp://localhost:21/redefine"
))
// redefine the service
ftpService = FtpService(restTemplate, urlProperties)
assertEquals("I'm fetching a file from ftp://localhost:21/redefine!",
ftpService.fetchFile())
}
#Test
fun `test default`() {
assertEquals("I'm fetching a file from default!",
ftpService.fetchFile())
}
#Test
fun `test fetch file root`() {
`when`(urlProperties.ftp)
.thenReturn(UrlProperties.FtpProperties(
url = "ftp://localhost:21"
))
assertEquals("I'm fetching a file from ftp://localhost:21!",
ftpService.fetchFile())
}
#Test
fun `test fetch file folder`() {
doReturn(
UrlProperties.FtpProperties(
url = "ftp://localhost:21/user/folder"
)).`when`(urlProperties).ftp
assertEquals("I'm fetching a file from ftp://localhost:21/user/folder!",
ftpService.fetchFile())
}
#Test
fun `test fetch file folder - reset`() {
Mockito.reset(urlProperties)
`when`(urlProperties.ftp)
.thenReturn(UrlProperties.FtpProperties(
url = "ftp://localhost:21/mockito/reset/when"
))
assertEquals("I'm fetching a file from ftp://localhost:21/mockito/reset/when!",
ftpService.fetchFile())
}
#Test
fun `test fetch file folder - reset & doReturn`() {
Mockito.reset(urlProperties)
doReturn(
UrlProperties.FtpProperties(
url = "ftp://localhost:21/reset/doReturn"
)).`when`(urlProperties).ftp
assertEquals("I'm fetching a file from ftp://localhost:21/reset/doReturn!",
ftpService.fetchFile())
}
}
Spring App | `src/main/kotlin/com/example/MyApp.kt`
package com.example
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.runApplication
#SpringBootApplication
#EnableConfigurationProperties
#ConfigurationPropertiesScan
class MyApp
fun main(args: Array<String>) {
runApplication<MyApp>(*args)
}
Example #Service | `src/main/kotlin/com/example/service/FtpService.kt`
package com.example.service
import org.springframework.stereotype.Service
import org.springframework.web.client.RestTemplate
#Service
class FtpService(
val restTemplate: RestTemplate,
urlProperties: UrlProperties,
val ftpProperties: UrlProperties.FtpProperties = urlProperties.ftp
) {
fun fetchFile(): String {
println(restTemplate)
return "I'm fetching a file from ${ftpProperties.url}!"
}
}
#ConfigurationProperties with #ConstructorBinding - `src/main/kotlin/com/example/service/UrlProperties.kt`
package com.example.service
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
#ConstructorBinding
#ConfigurationProperties("url")
data class UrlProperties(val ftp: FtpProperties) {
data class FtpProperties(
val url: String,
)
}

RabbitMQ conncetion refused

I am not able to conncet to RabbitMQ.RabbitMQ is not on local machine .
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
#Configuration
#EnableRabbit
public class AMQPConfig {
#Autowired
private RabbitMQProperties rabbitMQProperties;
#Bean
Queue queue() {
return new Queue(rabbitMQProperties.getQueueName(), false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange(rabbitMQProperties.getExchangeName());
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(rabbitMQProperties.getRoutingKey());
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
System.out.println( " ques name is ------- " + rabbitMQProperties.getQueueName() );
container.setQueueNames(rabbitMQProperties.getQueueName());
container.setMessageListener(listenerAdapter);
return container;
}
#Bean
public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
return new MappingJackson2MessageConverter();
}
#Bean
public RabbitTemplate amqpTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(messageConverter());
return rabbitTemplate;
}
#Bean
public Jackson2JsonMessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
#Bean
MessageListenerAdapter listenerAdapter(RabbitMqListener listener) {
return new MessageListenerAdapter(listener, "listen");
}
}
My application.properties looks like
spring.rabbitmq.password=pass
spring.rabbitmq.port=15671
spring.rabbitmq.username=user
spring.rabbitmq.host=https://urltologinscreen
I am able to access to Rabbitmq gui using
https://urltologinscreen:15671
I am getting the below error
ConfigServletWebServerApplicationContext
: Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name
'container' defined in class path resource [pathhidden/rabbitmq/AMQPConfig.class]: Unsatisfied dependency
expressed through
method 'container' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.springframework.amqp.rabbit.connection.ConnectionFactory'
available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Parameter 0 of method amqpTemplate in pathhidden.rabbitmq.AMQPConfig required a bean of type
'org.springframework.amqp.rabbit.connection.ConnectionFactory' that
could not be found.
How do i resolve the issue?
Here is a simple solution that worked for me
Make sure RabbitMQ is defined on your properties file
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
On your pom.xml file, ensure to add the tag below under dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
My guess is that you properties should be like so:
spring.rabbitmq.password=pass
spring.rabbitmq.username=user
spring.rabbitmq.host=urltologinscreen
Don't need to specify the port or use http in the host

Retrive JMS ConnectionFactory from JNDI using Spring Boot auto-configuration

I want to use the spring boot autoconfigure for JMS to connect to a remote JNDI and retrieve the ConnectionFactory based on his name populated through the spring.jms.jndi-name property in the application.properties file.
I noticed that the spring boot autoconfigure is relying on the JndiConnectionFactoryAutoConfiguration class to do that and this class in turn will call the JndiTemplate class to do the lookup. The problem is that the value of the environment attribute of the JndiTemplate class is null, so we cannot create the intialContext.
In fact, I noticed that the JndiTemplate class is instantiated with no-argument constructor in starting application and before loading the configuration defined in the JndiConnectionFactoryAutoConfiguration class.
My question: how can I instantiate JndiTemplate by specifying a list of properties (Context.INITIAL_CONTEXT_FACTORY, Context.PROVIDER_URL..)? knowing that JmsTemplate has a constructor that takes an Properties object.
Just for information: my application is a simple jar that doesn’t run on a server at the moment.
For those interested in the answer, you must use VM options to pass required JNDI properties.
Here is an example that works with ActiveMQ:
VM options:
-Djava.naming.provider.url=tcp://hostname:61616
-Djava.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
And spring properties file (application.properties) must contain the JNDI name of the connection factory:
spring.jms.jndi-name=ConnectionFactory
Much better, you can use configuration to fin your connection factory from JNDI. In my project, we finished by creating our jms starter that we can use in all microservices.
Properties class:
import lombok.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
#Getter
#Setter
#ToString
#NoArgsConstructor
#EqualsAndHashCode
#ConfigurationProperties( prefix = "custom.jms" )
public class CustomJmsProperties {
private String jndiName;
private String contextFactoryClass;
private String providerUrl;
private String username;
private String password;
}
Configuration class:
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jndi.JndiLocatorDelegate;
import javax.jms.ConnectionFactory;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Properties;
#Configuration
#ConditionalOnProperty( "custom.jms.jndi-name" )
#ConditionalOnMissingBean( ConnectionFactory.class )
#EnableConfigurationProperties( { CustomJmsProperties.class } )
#AutoConfigureAfter( { JndiConnectionFactoryAutoConfiguration.class } )
public class CustomJndiConnectionFactoryAutoConfiguration {
#Bean
public ConnectionFactory connectionFactory( CustomJmsProperties customJmsProperties ) throws NamingException {
ConnectionFactory connectionFactory = lookupForConnectionFactory( customJmsProperties );
return getEnhancedUserCredentialsConnectionFactory( customJmsProperties, connectionFactory );
}
private ConnectionFactory lookupForConnectionFactory( final CustomJmsProperties customJmsProperties ) throws NamingException {
JndiLocatorDelegate jndiLocatorDelegate = new JndiLocatorDelegate();
Properties jndiProperties = getJndiProperties( customJmsProperties );
jndiLocatorDelegate.setJndiEnvironment( jndiProperties );
return jndiLocatorDelegate.lookup( customJmsProperties.getJndiName(), ConnectionFactory.class );
}
private Properties getJndiProperties( final CustomJmsProperties customJmsProperties ) {
Properties jndiProperties = new Properties();
jndiProperties.setProperty( Context.PROVIDER_URL, customJmsProperties.getProviderUrl() );
jndiProperties.setProperty( Context.INITIAL_CONTEXT_FACTORY, customJmsProperties.getContextFactoryClass() );
if ( StringUtils.isNotEmpty( customJmsProperties.getUsername() ) ) {
jndiProperties.setProperty( Context.SECURITY_PRINCIPAL, customJmsProperties.getUsername() );
}
if ( StringUtils.isNotEmpty( customJmsProperties.getPassword() ) ) {
jndiProperties.setProperty( Context.SECURITY_CREDENTIALS, customJmsProperties.getPassword() );
}
return jndiProperties;
}
private UserCredentialsConnectionFactoryAdapter getEnhancedUserCredentialsConnectionFactory( final CustomJmsProperties customJmsProperties,
final ConnectionFactory connectionFactory ) {
UserCredentialsConnectionFactoryAdapter enhancedConnectionFactory = new UserCredentialsConnectionFactoryAdapter();
enhancedConnectionFactory.setTargetConnectionFactory( connectionFactory );
enhancedConnectionFactory.setUsername( customJmsProperties.getUsername() );
enhancedConnectionFactory.setPassword( customJmsProperties.getPassword() );
enhancedConnectionFactory.afterPropertiesSet();
return enhancedConnectionFactory;
}
}
Properties file of your project:
custom.jms.provider-url=tcp://hostname:61616
custom.jms.context-factory-class=org.apache.activemq.jndi.ActiveMQInitialContextFactory
custom.jms.jndi-name=ConnectionFactory

Injecting configuration dependency

I am creating a cache client wrapper using spring framework. This is to provide cache layer to our application. Right now, we are using redis. I have found out that spring-data-redis library is very good for creating my wrapper.
My application will pass a configuration POJO to my wrapper and will then use the interface that I will provide.
spring-data-redis provides an easy way to access redis using two variables.
RedisConnectionFactory
RedisTemplate<String, Object>
Although, I will be providing a better interface to my application with my interface functions like:
public Object getValue( final String key ) throws ConfigInvalidException;
public void setValue( final String key, final Object value ) throws ConfigInvalidException;
public void setValueWithExpiry(final String key, final Object value, final int seconds, final TimeUnit timeUnit) throws ConfigInvalidException;
I still want to provide RedisConnectionFactory and RedisTemplate beans.
My question is how to initialize my wrapper application with this configuration POJO?
Currently my configuration looks like this:
import java.util.List;
public class ClusterConfigurationProperties {
List<String> nodes;
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
And my AppConfig.java looks like this:
import com.ajio.Exception.ConfigInvalidException;
import com.ajio.configuration.ClusterConfigurationProperties;
import com.ajio.validator.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
#Configuration
public class AppConfig {
#Autowired
private ClusterConfigurationProperties clusterConfigurationProperties;
#Autowired
private Validator validator;
#Bean
ClusterConfigurationProperties clusterConfigurationProperties() {
return null;
}
#Bean
Validator validator() {
return new Validator();
}
#Bean
RedisConnectionFactory connectionFactory() throws ConfigInvalidException {
if (clusterConfigurationProperties == null)
throw new ConfigInvalidException("Please provide a cluster configuration POJO in context");
validator.validate(clusterConfigurationProperties);
return new JedisConnectionFactory(new RedisClusterConfiguration(clusterConfigurationProperties.getNodes()));
}
#Bean
RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) throws ConfigInvalidException {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory());
redisTemplate.setKeySerializer( new StringRedisSerializer() );
redisTemplate.setHashValueSerializer( new GenericToStringSerializer<>( Object.class ) );
redisTemplate.setValueSerializer( new GenericToStringSerializer<>( Object.class ) );
return redisTemplate;
}
}
Here I am expecting a ClusterConfigurationProperties POJO as a bean in application which will be using the interface of wrapper.
But to compile my wrapper, I have created a null bean itself. Then when application uses it, there will be two beans, one of application and one of wrapper.
How should I resolve this problem?
Actually what i wanted was to have cluster config as a bean in my client application. For that i dont need to declare #autowire clusterconfig in my wrapper application. Instead should take cluster config as a parameter in the method, so that the client will pass cluster config object when creating bean. And the bean which is created in client code should have code for creating redis connection factory.
But all this i was writing was to make my client unknown of redis. So, better solution is to have wrapper class which takes cluster config pojo and create redis connection factory etc. And client should create this wrapper as a bean.
Very poor concept of spring and design patterns lead me to this mistake.

Issue with Spring Data JPA - BeanEntityManagerFactory

I recently started learning spring. I am trying a simple example with spring data jpa in a spring mvc project. I am getting the following error while deploying the war file in the tomcat.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creati
ng bean with name '(inner bean)#584d15f2': Cannot resolve reference to bean 'ent
ityManagerFactory' while setting constructor argument; nested exception is org.s
pringframework.beans.factory.BeanCurrentlyInCreationException: Error creating be
an with name 'entityManagerFactory': Requested bean is currently in creation: Is
there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolve
ConstructorArguments(ConstructorResolver.java:634)
at org.springframework.beans.factory.support.ConstructorResolver.instant
iateUsingFactoryMethod(ConstructorResolver.java:444)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:11
19)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveInnerBean(BeanDefinitionValueResolver.java:299)
... 92 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: E
rror creating bean with name 'entityManagerFactory': Requested bean is currently
in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistr
y.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistr
y.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBe
an(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean
(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveReference(BeanDefinitionValueResolver.java:351)
... 100 more
27-Sep-2015 17:56:45.304 INFO [http-apr-8080-exec-35] org.apache.catalina.startu
p.HostConfig.deployWAR Deployment of web application archive D:\ApacheTomcat\apa
che-tomcat-8.0.26\webapps\springTest.war has finished in 6,124 ms
My controller code is as follows,
package com.demo.repo;
import com.demo.model.Customer;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import javax.activation.DataSource;
import javax.persistence.EntityManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
/**
* Handles requests for the application home page.
*/
#Controller
#Configuration
#EnableJpaRepositories("com.demo.repo")
#EnableTransactionManagement
public class HomeController {
#Autowired
customerRepository repository;
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
#Bean(destroyMethod = "close")
DataSource dataSource(Environment env) {
HikariConfig dataSourceConfig = new HikariConfig();
dataSourceConfig.setDriverClassName(env.getRequiredProperty("db.driver"));
dataSourceConfig.setJdbcUrl(env.getRequiredProperty("db.url"));
dataSourceConfig.setUsername(env.getRequiredProperty("db.username"));
dataSourceConfig.setPassword(env.getRequiredProperty("db.password"));
return (DataSource) new HikariDataSource(dataSourceConfig);
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource((javax.sql.DataSource) dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.demo.repo");
Properties jpaProperties = new Properties();
//Configures the used database dialect. This allows Hibernate to create SQL
//that is optimized for the used database.
jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
//Specifies the action that is invoked to the database when the Hibernate
//SessionFactory is created or closed.
jpaProperties.put("hibernate.hbm2ddl.auto",
env.getRequiredProperty("hibernate.hbm2ddl.auto")
);
//Configures the naming strategy that is used when Hibernate creates
//new database objects and schema elements
jpaProperties.put("hibernate.ejb.naming_strategy",
env.getRequiredProperty("hibernate.ejb.naming_strategy")
);
//If the value of this property is true, Hibernate writes all SQL
//statements to the console.
jpaProperties.put("hibernate.show_sql",
env.getRequiredProperty("hibernate.show_sql")
);
//If the value of this property is true, Hibernate will format the SQL
//that is written to the console.
jpaProperties.put("hibernate.format_sql",
env.getRequiredProperty("hibernate.format_sql")
);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
for(Customer customer : repository.findAll())
{
System.out.println("Log Results :: "+customer.toString());
}
return "myhome";
}
}
Can anyone suggest me what is wrong with my code and any suggestions to resolve the same.
It seems that your entityManagerFactory requires the dataSource that is defined in the same configuration file.
Try moving the definition of dataSource to another configuration class, or, instead of passing the dataSource as parameter, just call the dataSource() method when you need it in the entityManagerFactory.
#Autowired
Environment env;
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource((javax.sql.DataSource) dataSource());
....
}
TIP: Don't mix your #Controller and #Configuration. Create a different file for each of them.

Resources