"No bean named 'cassandraTemplate' available" - spring

I have not added cassandra template bean in my beandefinition class, but it works fine and gives me the required output while running, but while writing junit test class
it is throwing me a error "No bean named 'cassandraTemplate' available".
Why is this issue raising during running my junit test class.
This is my code:
#Configuration
#PropertySource("cassandra.properties")
#EnableCassandraRepositories(basePackages = "...repository")
public class Beandef
{
#Autowired
public Environment environment;
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
#Bean(name = "clusterFactory")
public CassandraClusterFactoryBean getCluster() {
PoolingOptions poolingOptions = new PoolingOptions();
cluster.setContactPoints(environment.getProperty("cassandra.contactpoints"));
cluster.setPoolingOptions(poolingOptions);
cluster.setPort(Integer.parseInt(environment.getProperty("cassandra.port")));
poolingOptions.setNewConnectionThreshold(HostDistance.LOCAL, 50);
return cluster;
}
#Bean
#DependsOn("clusterFactory")
public CassandraSessionFactoryBean getSession() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster.getObject());
session.setKeyspaceName(environment.getProperty("cassandra.keyspace"));
session.setConverter(new MappingCassandraConverter(new CassandraMappingContextAware()));
session.setSchemaAction(SchemaAction.NONE);
return session;
}

If you're using Spring Boot, and if your test class doesn't have a #SpringBootApplication available in the classpath, you have to add #EnableAutoConfiguration to enable Spring Boot auto-config (even with spring-boot-starter-data-cassandra in your dependencies).

Related

SpringBootTest, Testcontainers, container start up - Mapped port can only be obtained after the container is started

I am using docker/testcontainers to run a postgresql db for testing. I have effectively done this for unit testing that is just testing the database access. However, I have now brought springboot testing into the mix so I can test with an embedded web service and I am having problems.
The issue seems to be that the dataSource bean is being requested before the container starts.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [com/myproject/integrationtests/IntegrationDataService.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Caused by: java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
Here is my SpringBootTest:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = {IntegrationDataService.class, TestApplication.class},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringBootTestControllerTesterIT
{
#Autowired
private MyController myController;
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
public void testRestControllerHello()
{
String url = "http://localhost:" + port + "/mycontroller/hello";
ResponseEntity<String> result =
restTemplate.getForEntity(url, String.class);
assertEquals(result.getStatusCode(), HttpStatus.OK);
assertEquals(result.getBody(), "hello");
}
}
Here is my spring boot application referenced from the test:
#SpringBootApplication
public class TestApplication
{
public static void main(String[] args)
{
SpringApplication.run(TestApplication.class, args);
}
}
Here is the IntegrationDataService class which is intended to startup the container and provide the sessionfactory/datasource for everything else
#Testcontainers
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#EnableTransactionManagement
#Configuration
public class IntegrationDataService
{
#Container
public static PostgreSQLContainer postgreSQLContainer = (PostgreSQLContainer) new PostgreSQLContainer("postgres:9.6")
.withDatabaseName("test")
.withUsername("sa")
.withPassword("sa")
.withInitScript("db/postgresql/schema.sql");
#Bean
public Properties hibernateProperties()
{
Properties hibernateProp = new Properties();
hibernateProp.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
hibernateProp.put("hibernate.format_sql", true);
hibernateProp.put("hibernate.use_sql_comments", true);
// hibernateProp.put("hibernate.show_sql", true);
hibernateProp.put("hibernate.max_fetch_depth", 3);
hibernateProp.put("hibernate.jdbc.batch_size", 10);
hibernateProp.put("hibernate.jdbc.fetch_size", 50);
hibernateProp.put("hibernate.id.new_generator_mappings", false);
// hibernateProp.put("hibernate.hbm2ddl.auto", "create-drop");
// hibernateProp.put("hibernate.jdbc.lob.non_contextual_creation", true);
return hibernateProp;
}
#Bean
public SessionFactory sessionFactory() throws IOException
{
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setHibernateProperties(hibernateProperties());
sessionFactoryBean.setPackagesToScan("com.myproject.model.entities");
sessionFactoryBean.afterPropertiesSet();
return sessionFactoryBean.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() throws IOException
{
return new HibernateTransactionManager(sessionFactory());
}
#Bean
public DataSource dataSource()
{
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(postgreSQLContainer.getDriverClassName());
dataSource.setUrl(postgreSQLContainer.getJdbcUrl());
dataSource.setUsername(postgreSQLContainer.getUsername());
dataSource.setPassword(postgreSQLContainer.getPassword());
return dataSource;
}
}
The error occurs on requesting the datasource bean from the sessionFactory from one of the Dao classes before the container starts up.
What the heck am I doing wrong?
Thanks!!!
The reason for your java.lang.IllegalStateException: Mapped port can only be obtained after the container is started exception is that when the Spring Context now gets created during your test with #SpringBootTest it tries to connect to the database on application startup.
As you only launch your PostgreSQL inside your IntegrationDataService class, there is a timing issue as you can't obtain the JDBC URL or create a connection on application startup as this bean is not yet properly created.
In general, you should NOT use any test-related code inside your IntegrationDataService class. Starting/stopping the database should be done inside your test setup.
This ensures to first start the database container, wait until it's up- and running, and only then launch the actual test and create the Spring Context.
I've summarized the required setup mechanism for JUnit 4/5 with Testcontainers and Spring Boot, that help you get the setup right.
In the end, this can look like the following
// JUnit 5 example with Spring Boot >= 2.2.6
#Testcontainers
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationIT {
#Container
public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer()
.withPassword("inmemory")
.withUsername("inmemory");
#DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
}
#Test
public void contextLoads() {
}
}

spring-boot create bean only when action is called

I have some some spring configuration code which creates the spring bean
#Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
In some class I am using like below
#Autowired
private MongoTemplate mongoTemplate ;
Bean in getting created whenever spring is started but due to some some service I want to make sure bean should be created only when action is invoked on the object
like mongoTemplate.save etc
Proxy by CGLIB and lazy-initialization are available.
#Lazy
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/core.html#beans-factorybeans-annotations

Could not autowire. No beans of 'InstructionRepository' type found

Trying to create a bean in SpringBoot application, but getting the following error "Could not autowire. No beans of 'InstructionRepository' type found."
InstructionRepository is annotated with #Repository annotation in the jar and is an Interface extending a Spring Data Interface
ScheduleProcessor is a method
When I Try adding the #ComponentScan annotation by passing the base package value, the error goes away BUT, when I boot up the application get the following error
Parameter 0 of constructor in com.xxx.resync.config.AppConfig required a bean of type 'com.xxx.repo.InstructionRepository' that could not be found. Action: Consider defining a bean of type 'com.xxx.repo.InstructionRepository' in your configuration.
#Configuration
#EnableAutoConfiguration
//#ComponentScan(basePackages = {"com.xxx.repo"})
public class AppConfig {
#Value("${pssHttp.connectTimeout:3000}")
private int connectTimeout;
#Bean
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(connectTimeout);
factory.setReadTimeout(connectTimeout);
restTemplate.setRequestFactory(factory);
return restTemplate;
}
#Bean
public ScheduleUpdater getScheduleUpdater() {
return new ScheduleUpdater(true);
}
#Bean
public ScheduleProcessor scheduleProcessor(InstructionRepository instructionRepository, ScheduleUpdater scheduleUpdater) {
return new ScheduleProcessor(instructionRepository, scheduleUpdater);
}
}
InstructionRepository
#Repository
public interface InstructionRepository extends CouchbaseRepository<Instruction, String> {
}
How can we fix the error and be able to boot up the Spring boot application?
Any suggestions appreciated.
You need to add #EnableCouchbaseRepositories to enable repo building eg to AppConfig.

SpringBoot app. accessing 2 DataSources using JdbcTemplate

I have a SpringBoot app. that has to access to different datasources to export data from 1 to another (1 local datasource and another remote datasource)
This is how my persistenceConfig looks like
public class PersistenceConfig {
#Bean
public JdbcTemplate localJdbcTemplate() {
return new JdbcTemplate(localDataSource());
}
#Bean
public JdbcTemplate remoteJdbcTemplate() {
return new JdbcTemplate(remoteDataSource());
}
#Bean
public DataSource localDataSource(){
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(getLocalDbPoolSize());
config.setMinimumIdle(5);
config.setDriverClassName(getLocalDbDriverClassName());
config.setJdbcUrl(getLocalDbJdbcUrl());
config.addDataSourceProperty("user", getLocalDbUser());
config.addDataSourceProperty("password", getLocalDbPwd());
return new HikariDataSource(config);
}
#Bean
public DataSource remoteDataSource(){
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(getRemoteDbPoolSize());
config.setMinimumIdle(5);
config.setDriverClassName(getRemoteDbDriverClassName());
config.setJdbcUrl(getRemoteDbJdbcUrl());
config.addDataSourceProperty("user", getRemoteDbUser());
config.addDataSourceProperty("password", getRemoteDbPwd());
return new HikariDataSource(config);
}
}
But when I init my app I got this error:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 2: localDataSource,remoteDataSource
I also tried to user qualified beans, as follows:
#Bean(name = "localJdbcTemplate")
public JdbcTemplate localJdbcTemplate() {
return new JdbcTemplate(localDataSource());
}
#Bean(name = "remoteJdbcTemplate")
public JdbcTemplate remoteJdbcTemplate() {
return new JdbcTemplate(remoteDataSource());
}
#Bean(name = "localDataSource")
public DataSource localDataSource(){
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(getLocalDbPoolSize());
config.setMinimumIdle(5);
config.setDriverClassName(getLocalDbDriverClassName());
config.setJdbcUrl(getLocalDbJdbcUrl());
config.addDataSourceProperty("user", getLocalDbUser());
config.addDataSourceProperty("password", getLocalDbPwd());
return new HikariDataSource(config);
}
#Bean(name = "remoteDataSource")
public DataSource remoteDataSource(){
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(getRemoteDbPoolSize());
config.setMinimumIdle(5);
config.setDriverClassName(getRemoteDbDriverClassName());
config.setJdbcUrl(getRemoteDbJdbcUrl());
config.addDataSourceProperty("user", getRemoteDbUser());
config.addDataSourceProperty("password", getRemoteDbPwd());
return new HikariDataSource(config);
}
but then I got this other error:
A component required a bean of type 'org.springframework.transaction.PlatformTransactionManager' that could not be found.
- Bean method 'transactionManager' not loaded because #ConditionalOnSingleCandidate (types: javax.sql.DataSource; SearchStrategy: all) did not find a primary bean from beans 'remoteDataSource', 'localDataSource'
I also tried
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class})
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class})
but then I got
A component required a bean of type 'org.springframework.transaction.PlatformTransactionManager' that could not be found.
You can use bean names to qualify them:
#Bean(name = "localDataSource")
public DataSource localDataSource() {
...
}
#Bean(name = "remoteDataSource")
public DataSource remoteDataSource() {
...
}
Please note: You have to do the same for your JdbcTemplate beans - just give them a name and it will work.
See the Spring JavaDoc for more Information: Bean
#Bean(name = "localJdbcTemplate")
public JdbcTemplate localJdbcTemplate() {
return new JdbcTemplate(localDataSource());
}
When you use your JdbcTemplate beans within your export service implementation via autowiring (#Autowired), you need to use #Qualifier to qualify them:
#Autowired
#Qualifier("localJdbcTemplate")
private JdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("remoteJdbcTemplate")
private JdbcTemplate jdbcTemplate;
Bean gets its name from the method name, providing name attribute just makes it explicit (keeping the name the same as the method name). Overall suggestion about #Bean(name="...") and #Qualifier didn't fix the error for me.
I set up sample project with two embedded databases and got the same error as the aothor. Spring suggestion was to annotate one of the DataSource beans as #Primary and, in fact, this fixes the error. Usually it happens, when some other application parts want to see only one or one primary DataSource, if several present.
What seems to be a better solution is to disable not needed autoconfiguration beans keeping rest of the code as it is:
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class})
or:
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class})
depending on which annotations are in use.
If author doesn't use any JPA provider and operates directly with JdbcTemplate it may be a suitable solution.

Bean reference overridden by non-compatible bean instance of type [com.sun.proxy.$Proxy159]

I am a beginner in developing web services and I have a jaxrs web service which has the folllowing config :
#Configuration
#ComponentScan("com.example.service")
#ComponentScan("com.example.services")
#ImportResource({
"classpath:/META-INF/cxf/cxf.xml",
"classpath:/META-INF/cxf/cxf-servlet.xml"
})
public class AppConfig {
#Bean(destroyMethod = "shutdown")
public SpringBus cxf() {
return new SpringBus();
}
#Bean
public Server jaxRsServer() {
//Define swagger feature
Swagger2Feature feature = new Swagger2Feature();
//REST Factory with all services,providers and features
JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint(jaxRsApiApplication(), JAXRSServerFactoryBean.class);
factory.setServiceBeans(Arrays.asList(baseRestService(), materialsRestService(), batchRestService(), billingRestService(), locationRestService(), customerRestService(), equipmentRestService(), projectRestService(), reservationRestService(), waferRestService()));
factory.setAddress(factory.getAddress());
factory.setProviders(Arrays.asList(jsonProvider(), authenticationService()));
factory.getFeatures().add(feature);
return factory.create();
}
#Bean
public JaxRsApiApplication jaxRsApiApplication() {
return new JaxRsApiApplication();
}
#Bean
public JacksonJsonProvider jsonProvider() {
return new JacksonJsonProvider();
}
#Bean
public AuthenticationService authenticationService() {
return new AuthenticationService();
}
**all other beans**
Recently I started getting the following exception:
java.lang.IllegalStateException: #Bean method AppConfig.materialsRestService called as a bean reference for type [com.phoenixbv.rs.MaterialsRestService] but overridden by non-compatible bean instance of type [com.sun.proxy.$Proxy159]. Overriding bean of same name declared in: com.example.config.AppConfig
I would appreciate any help !
I was able to solve the problem by creating Interfaces to the service classes and the injecting the interfaces into the factory.

Resources