VFS resource required a bean named 'omapmDataSource' that could not be found - spring

I am new to Spring Boot. The code is Sring Batch which converts data from Outsystems database to xmls.
I am upgrading the code from spring boot 1.4.0 to the latest 2.7.2.
Currently using JDK8 & Gradle 6.9
The code gets deployed to wildfly (RHEL7/Jboss7)
I have migrated the code but the logs show some issues while starting the service. It cannot find Bean "omapmDataSource" but it is clearly defined.
It says: Failed to instantiate [org.mybatis.spring.SqlSessionFactoryBean]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'omapmDataSource' available
The build.gradle file:
buildscript {
repositories {
maven { url "https://jcenter.bintray.com" }
mavenCentral()
}
dependencies {
classpath ("org.springframework.boot:spring-boot-gradle-plugin:2.7.2")
}
}
apply plugin: 'java-library'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'war'
apply plugin: 'project-report'
sourceCompatibility = 1.8
targetCompatibility = 1.8
version = '1.5'
war {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart',
'Implementation-Version': version
}
archiveName 'ApplicationDataExtractor.war'
metaInf { from 'src/main/resources/META-INF/*' }
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-batch:2.7.2'
implementation 'org.springframework.boot:spring-boot-maven-plugin:2.7.2'
implementation 'org.springframework.security:spring-security-crypto:5.7.2'
implementation 'org.bouncycastle:bcpg-jdk15on:1.70'
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
implementation 'org.springframework.security:spring-security-web:5.7.2'
implementation 'org.springframework.security:spring-security-config:5.7.2'
implementation 'org.springframework.boot:spring-boot-starter-actuator:2.7.2'
//implementation 'org.hsqldb:hsqldb:2.7.2'
implementation 'org.hsqldb:hsqldb:2.5.2'
implementation 'org.springframework.boot:spring-boot-starter-web:2.7.2'
implementation 'org.jasypt:jasypt:1.9.3'
implementation 'org.springframework.boot:spring-boot-starter-batch:2.7.2'
implementation 'org.springframework:spring-oxm:5.3.22'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
implementation 'org.mybatis:mybatis:3.5.10'
implementation files('jdbclibs/ojdbc7.jar')
implementation files('jdbclibs/ucp.jar')
implementation files('jdbclibs/bcprov-jdk15on-155.jar')
implementation files('jdbclibs/CouchbaseIntegration-1.04.jar')
implementation 'org.springframework.data:spring-data-couchbase:4.4.2'
implementation 'org.springframework.boot:spring-boot-starter-data-couchbase:2.7.2'
implementation 'com.couchbase.client:java-client:3.3.3'
implementation 'org.springframework.boot:spring-boot-starter-parent:2.7.2'
implementation group: 'commons-collections', name: 'commons-collections', version: '3.2.2'
implementation group: 'commons-configuration', name: 'commons-configuration', version: '1.10'
implementation 'org.slf4j:jcl-over-slf4j:1.7.36'
testImplementation group: 'junit', name: 'junit', version: '4.13.2'
}
test {
systemProperties 'property': 'value'
}
task copyRuntimeLibs(type: Copy) {
into "lib"
from configurations.runtime
}
task allDeps {
doLast {
println "All Dependencies:"
allprojects.each { p ->
println()
println " $p.name ".center( 60, '*' )
println()
p.configurations.all.findAll { !it.allDependencies.empty }.each { c ->
println " ${c.name} ".center( 60, '-' )
c.allDependencies.each { dep ->
println "$dep.group:$dep.name:$dep.version"
}
println "-" * 60
}
}
}
}
springBoot {
mainClass = "com.*SomePackageName*.om.apm.batch.ApplicationDataExtractorBoot"
}
repositories {
flatDir {
dirs 'repos'
}
}
The Data Source Configuration File consisting the Bean(Pls ignore SomePackageName, I replaced it from actual name):
package com.*SomePackageName*.om.apm.batch.config;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.stereotype.Component;
import oracle.jdbc.pool.OracleDataSource;
#Configuration
#Component
#MapperScan("com.*SomePackageName*.om.apm.mapping")
#ComponentScan({"com.*SomePackageName*"})
#EntityScan(basePackages = "com.*SomePackageName*")
public class DataSourceConfiguration {
private final static Logger logger = LoggerFactory.getLogger(DataSourceConfiguration.class);
#Autowired
private Environment environment;
#Bean(name = "omapmDataSource")
#Profile("server")
public DataSource omapmDataSource() {
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
final String dataSourceName = this.environment.getProperty("omapm.datasource");
DataSource dataSource = dsLookup.getDataSource(dataSourceName);
return dataSource;
}
#Bean(name = "omapmDataSource")
#Profile("mock")
public DataSource omapmDataSourceMock() {
final String user = this.environment.getProperty("omapm.jdbc.user");
final String password = this.environment.getProperty("omapm.jdbc.password");
final String url = this.environment.getProperty("omapm.jdbc.url");
return this.oracleDataSource(url, user, password);
}
private DataSource oracleDataSource(String url, String user, String password) {
OracleDataSource dataSource = null;
try {
dataSource = new OracleDataSource();
dataSource.setURL(url);
dataSource.setUser(user);
dataSource.setPassword(password);
} catch (SQLException e) {
logger.error("ERROR : ", e);
e.printStackTrace();
}
return dataSource;
}
#Bean(name = "batchDataSource")
#Primary
//#Profile("mock")
public DataSource batchDataSourceMock() {
final SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriver(new org.hsqldb.jdbcDriver());
//dataSource.setDriverClass());
dataSource.setUrl("jdbc:hsqldb:file:/apps/hsqldb/hsqldb");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
#Autowired
private ResourceLoader resourceLoader;
#Bean
public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(omapmDataSource());
sessionFactory.setTypeAliasesPackage("com.*SomePackageName*.om.apm.batch.domain");
sessionFactory.setMapperLocations(new Resource[] {
new ClassPathResource("entity-mapper.xml"),
new ClassPathResource("application-mapper.xml"),
new ClassPathResource("iamapplication-mapper.xml"),
new ClassPathResource("riskletter-mapper.xml"),
new ClassPathResource("accesshistory-mapper.xml"),
new ClassPathResource("hotpeople-mapper.xml"),
new ClassPathResource("referencecode-mapper.xml"),
new ClassPathResource("lettertable-mapper.xml"),
new ClassPathResource("userRoles-mapper.xml")});
return sessionFactory;
}
}
ApplicationDataExtractorBatchConfiguration file which imports stuff from DataSourceConfiguration, etc:
#Configuration
#EnableBatchProcessing
#EnableAsync
#Component
#Import({ DataSourceConfiguration.class, CouchbaseConfiguration.class, BusinessBureauDataCouchbaseConfiguration.class,
ActivityLogCouchbaseConfiguration.class, CRACouchbaseConfiguration.class,
GeneratedDocsCouchbaseConfiguration.class })
#PropertySource("file:/usr/share/jbossas/standalone/configuration/application.properties")
#PropertySource("file:/usr/share/jbossas/standalone/configuration/product.properties")
public class ApplicationDataExtractorBatchConfiguration extends DefaultBatchConfigurer {
private final static Logger logger = LoggerFactory.getLogger(ApplicationDataExtractorBatchConfiguration.class);
// private static int CHUNK_SIZE = 30;
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Autowired
private PlatformTransactionManager transactionManager;
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
#Autowired
private Environment environment;
#Override
#Autowired
public void setDataSource(#Qualifier("batchDataSource") DataSource batchDataSource) {
super.setDataSource(batchDataSource);
}
#Bean
public ResourcelessTransactionManager resourceTransactionManager() {
return new ResourcelessTransactionManager();
}
#Bean
public MapJobRepositoryFactoryBean mapJobRepositoryFactory(
ResourcelessTransactionManager resourceTransactionManager) throws Exception {
MapJobRepositoryFactoryBean factory = new MapJobRepositoryFactoryBean(
resourceTransactionManager);
factory.afterPropertiesSet();
return factory;
}
#Bean
public JobRepository jobRepository(MapJobRepositoryFactoryBean factory)
throws Exception {
return factory.getObject();
}
#Bean
public SimpleJobLauncher jobLauncher(JobRepository jobRepository) {
SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobRepository(jobRepository);
return launcher;
}
}
Error Logs:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-08-08 08:43:03,073 ERROR [org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter] (ServerService Thread Pool -- 46)
***************************
APPLICATION FAILED TO START
***************************
Description:
VFS resource ["/content/ApplicationDataExtractor.war/WEB-INF/classes/com/*SomePackageName*/om/apm/mapping/EntityMapper.class"] required a bean named 'omapmDataSource' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'omapmDataSource' in your configuration.
2022-08-08 08:43:03,074 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 46) MSC000001: Failed to start service jboss.deployment.unit."ApplicationDataExtractor.war".undertow-deployment: org.jboss.msc.service.StartException in service jboss.deployment.unit."ApplicationDataExtractor.war".undertow-deployment: java.lang.RuntimeException: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'batchTriggerForCSVFile': Unsatisfied dependency expressed through field 'riskLettersSummaryDataExtractorStep'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'riskLettersSummaryDataExtractorStep' defined in class path resource [com/*SomePackageName*/om/apm/batch/config/ApplicationDataExtractorBatchConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.core.Step]: Factory method 'riskLettersSummaryDataExtractorStep' threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'riskLettersDataReader': Unsatisfied dependency expressed through field 'entityMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityMapper' defined in VFS resource ["/content/ApplicationDataExtractor.war/WEB-INF/classes/com/*SomePackageName*/om/apm/mapping/EntityMapper.class"]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/*SomePackageName*/om/apm/batch/config/DataSourceConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.mybatis.spring.SqlSessionFactoryBean]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'omapmDataSource' available
For full logs: https://drive.google.com/file/d/1g7hX2vFZR1THhTpKBeW10IEh_xqtZ97Q/view?usp=sharing

The omapmDataSource dataSource bean is defined under the server profile. Accoding to the error, this bean is not defined, probably because you are starting your application with the default profile (in which that bean won't be visible).
Make sure you start your application with the server profile, or make that bean available to the default profile.

Related

Spring Batch test case with multiple data sources

I have a Spring Batch Classifier to test for which I've defined this test class:
#RunWith(SpringRunner.class)
#SpringBatchTest
#ContextConfiguration(classes = { BatchConfiguration.class })
class CsvOutputClassifierTest {
#Autowired
private FlatFileItemWriter<CsvData> createRequestForProposalWriter;
#Autowired
private FlatFileItemWriter<CsvData> createRequestForQuotationWriter;
private final CsvOutputClassifier csvOutputClassifier = new CsvOutputClassifier(
createRequestForProposalWriter,
createRequestForQuotationWriter);
#Test
void shouldReturnProposalWriter() {
...
}
The batch configuration class has this constructor:
public BatchConfiguration(
final JobBuilderFactory jobBuilderFactory,
final StepBuilderFactory stepBuilderFactory,
#Qualifier("oerationalDataSource") final DataSource oerationalDataSource,
final DwhFileManager dwhFileManager,
final OperationalRepository operationalRepository)
And these beans:
#StepScope
#Bean
public FlatFileItemWriter<CsvData> createRequestForProposalWriter(
#Value("#{jobParameters['startDate']}") String startDate) {
FlatFileItemWriter<CsvData> writer = new FlatFileItemWriter<CsvData>();
...
return writer;
}
#StepScope
#Bean
public FlatFileItemWriter<CsvData> createRequestForQuotationWriter(
#Value("#{jobParameters['startDate']}") String startDate) {
FlatFileItemWriter<CsvData> writer = new FlatFileItemWriter<CsvData>();
...
return writer;
}
Running the test class I'm not able to trigger the first test method as I'm getting:
Error creating bean with name 'batchConfiguration': Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value="oerationalDataSource")}
In fact, I defined two different data sources, one for the 'operational' data and the 'app' for Spring Batch persistency:
#Configuration(proxyBeanMethods = false)
public class DataSourceConfiguration {
#Bean
#Primary
#ConfigurationProperties("app.datasource")
public DataSourceProperties defaultDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.configuration")
public HikariDataSource defaultDataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
}
#Bean
#ConfigurationProperties("aggr.datasource")
public DataSourceProperties oerationalDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("aggr.datasource.configuration")
public HikariDataSource oerationalDataSource(
#Qualifier("oerationalDataSourceProperties") DataSourceProperties oerationalDataSourceProperties) {
return oerationalDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Bean
public JdbcTemplate operationalJdbcTemplate(#Qualifier("oerationalDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
In #SpringBatchTest documentation it is reported that just one DataSource should be found or it should be marked as Primary:
It should be noted that JobLauncherTestUtils requires a org.springframework.batch.core.Job bean and that JobRepositoryTestUtils requires a javax.sql.DataSource bean. Since this annotation registers a JobLauncherTestUtils and a JobRepositoryTestUtils in the test context, it is expected that the test context contains a single autowire candidate for a org.springframework.batch.core.Job and a javax.sql.DataSource (either a single bean definition or one that is annotated with org.springframework.context.annotation.Primary).
But I have it. So how to fix it?
Update #1
Thanks to #Henning's tip, I've changed the annotations as follows:
#RunWith(SpringRunner.class)
#SpringBatchTest
#SpringBootTest(args={"--mode=custom", "--startDate=2022-05-31T01:00:00.000Z", "--endDate=2022-05-31T23:59:59.999Z"})
#ContextConfiguration(classes = { BatchConfiguration.class, DataSourceConfiguration.class, LocalFileManager.class, AggregatorRepository.class })
#ActiveProfiles({"integration"})
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
Where:
#SpringBootTest is needed to avoid 'Failed to determine a suitable driver class exception'
args is needed to provide the required parameters to the batch
But still having this exception:
Error creating bean with name 'scopedTarget.createRequestForProposalWriter' defined in BatchConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.item.file.FlatFileItemWriter]: Factory method 'createRequestForProposalWriter' threw exception; nested exception is java.lang.NullPointerException: text
raised in the implementation of:
#StepScope
#Bean
public FlatFileItemWriter<CsvData> createRequestForProposalWriter(
#Value("#{jobParameters['startDate']}") String startDate)
as the parameter 'startDate' is null.
In my naivety I assumed that I could test in isolation the classifier with something like that:
#Test
void shouldReturnProposalWriter() {
CsvData csvData = create-some-fake-data
CsvOutputClassifier csvOutputClassifier = new CsvOutputClassifier(
createRequestForProposalWriter,
createRequestForQuotationWriter);
ItemWriter itemWriter = csvOutputClassifier.classify(csvData);
some-assert-about-itemWriter-properties
}
So now the question is: how to correctly test the classifier?
You need to list DataSourceConfiguration as argument of #ContextConfiguration, i.e. your test class should start like this
#RunWith(SpringRunner.class)
#SpringBatchTest
#ContextConfiguration(classes = { BatchConfiguration.class, DataSourceConfiguration.class })
class CsvOutputClassifierTest {
...
}
The DataSourceConfiguration is currently not known within the test as you didn't declare it as part of the context or enabled classpath scanning in any form.

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

cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'MyTastTasklet'

I am developing Spring Boot Batch Example. In this example, I have created BatchJPA core module which has Entities, JPARepository and DB configurations.
This module adding into another Spring Module as dependency and in this module, I am adding code related specific batch jobs (like custom repository etc). I have total 15 batch jobs and I will be creating separate Spring Boot project with BatchJPA dependency.
10-08-2018 14:54:11.853 [main] WARN org.springframework.context.support.ClassPathXmlApplicationContext.refresh - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'MyTestTasklet': Initialization of bean failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'transactionManager' available
10-08-2018 14:54:11.855 [main] INFO org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener.logAutoConfigurationReport -
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
10-08-2018 14:54:11.919 [main] ERROR org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter.report -
***************************
APPLICATION FAILED TO START
***************************
Description:
A component required a bean named 'transactionManager' that could not be found.
Action:
Consider defining a bean named 'transactionManager' in your configuration.
Code below:
#Service
public class MyTaskTasklet implements Tasklet {
#Autowired
private MyCustomerCustomRepository myCustomerCustomRepository;
#Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
List<MyTest> mydata = myCustomerCustomRepository.getElligibleData();
if (!mydata.isEmpty()) {
System.out.println("XXXXXX = " + mydata.size());
}
chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext()
.put("listOfData", mydata);
return RepeatStatus.FINISHED;
}
}
Another file:
#Component
#EnableBatchProcessing
public class MyJobLauncher {
public void executeJob() {
String[] springConfig = { "jobs/ABC.xml"};
ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean("MyJobId");
try {
JobParameters jobParameters = new JobParametersBuilder().addString("runMode", "DATA")
.addDate("date", new Date()).addLong("time", System.currentTimeMillis()).toJobParameters();
jobLauncher.run(job, jobParameters);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
and another file
MainApplication
#SpringBootApplication
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
public class MainApplication implements CommandLineRunner {
#Autowired
private MyJobLauncher jobLauncher;
public static void main(String[] args) {
SpringApplication.run(MyJobLauncher.class, args);
}
#Override
public void run(String... args) throws Exception {
jobLauncher.executeJob();
}
}

How to configure EntityManagerFactoryBuilder bean when testing Spring Boot Batch application?

I have a Spring Boot Batch application that I'm writing integration tests against. However, I'm getting the following error about the EntityManagerFactoryBuilder bean missing when running a test:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'entityManagerFactory' defined in com.example.DatabaseConfig: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder]: :
No qualifying bean of type [org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749) ~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
My understanding is that Spring Boot provides the EntityManagerFactoryBuilder bean on application startup. How can I have the EntityManagerFactoryBuilder provided when running tests?
Here's my test code:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {DatabaseConfig.class, BatchConfiguration.class})
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
StepScopeTestExecutionListener.class })
public class StepScopeTestExecutionListenerIntegrationTests {
#Autowired
private FlatFileItemReader<Foo> reader;
#Rule
public TemporaryFolder testFolder = new TemporaryFolder();
public StepExecution getStepExection() {
StepExecution execution = MetaDataInstanceFactory.createStepExecution();
return execution;
}
#Test
public void testGoodData() throws Exception {
//some test code
}
Here's the DatabaseConfig class:
#Configuration
#EnableJpaRepositories(basePackages={"com.example.repository"},
entityManagerFactoryRef="testEntityManagerFactory",
transactionManagerRef = "testTransactionManager")
public class DatabaseConfig {
#Bean
public LocalContainerEntityManagerFactoryBean testEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder
.dataSource(dataSource())
.packages("com.example.domain")
.persistenceUnit("testLoad")
.build();
}
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public PlatformTransactionManager testTransactionManager(EntityManagerFactory testEntityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(testEntityManagerFactory);
return transactionManager;
}
}
When (integration) testing a Spring Boot application that is what you should do. The #SpringApplicationConfiguration is intended to take your application class (the one with the #SpringBootApplication annotation) as it will then be triggered to do much of the same auto configuration as a regular Spring Boot application.
You are only including 2 configuration classes and as such no auto configuration will be done.

Spring Data: inject 2 repositories with same name but in 2 different packages

Context
I want to use in the same Spring context two different databases that have entities that share the same name, but not the same structure. I rely on Spring Data MongoDB and JPA/JDBC. I have two packages, containing among others the following files:
com.bar.entity
Car.class
com.bar.repository
CarRepository.class
RepoBarMarker.class
com.bar.config
MongoConfiguration.class
com.foo.entity
Car.class
com.foo.repository
CarRepository.class
RepoFooMarker.class
com.foo.config
JPAConfiguration.class
SpecEntityManagerFactory.class
The content of each Car.class is different, I cannot reuse them. bar uses Spring-Mongo and foo uses Spring-JPA, and repositories are initialised via #EnableMongoRepositories and #EnableJpaRepositories annotations. When in one of my application component I try to access the foo version of the repository:
#Resource
private com.foo.repository.CarRepository carRepository;
I have the following exception when the class containing the #Resource field is created:
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'carRepository' must be of type [com.foo.repository.CarRepository], but was actually of type [com.sun.proxy.$Proxy31]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:374)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:446)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:420)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:545)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:155)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:305)
... 26 more
It appears that Spring tries to convert a bar repository to a foo repository, instead of creating a new bean, as in the same stack I also have the following exception:
Caused by: java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy31 implementing com.bar.repository.CarRepository,org.springframework.data.repository.Repository,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [com.foo.repository.CarRepository]: no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:267)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:93)
at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64)
... 35 more
If I try instead to autowire the repository:
#Autowire
private com.foo.repository.CarRepository carRepository;
I get the following exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.foo.CarRepository com.shell.ShellApp.carRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.foo.CarRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:509)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:290)
... 26 more
Spring-data configuration
In foo (JPA) package, JPAConfigration.class:
#Configuration
#EnableJpaRepositories(basePackageClasses = RepoFooMarker.class)
public class JPAConfiguration {
#Autowired
public DataSource dataSource;
#Autowired
public EntityManagerFactory entityManagerFactory;
#Bean
public EntityManager entityManager(final EntityManagerFactory entityManagerFactory) {
return entityManagerFactory.createEntityManager();
}
#Bean
public Session session(final EntityManager entityManager)
{
return entityManager.unwrap(Session.class);
}
#Bean
public PlatformTransactionManager transactionManager() throws SQLException {
final JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
SpecEntityManagerFactory.class:
#Configuration
public class SpecEntityManagerFactory {
#Bean
public EntityManagerFactory entityManagerFactory(final DataSource dataSource) throws SQLException {
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setDatabase(Database.POSTGRESQL);
final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("com.foo.entity");
factory.setJpaProperties(getHibernateProperties());
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return factory.getObject();
}
private Properties getHibernateProperties()
{
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
return hibernateProperties;
}
}
In bar (MongoDB) package, MongoConfiguration.class:
#Configuration
#EnableMongoRepositories(basePackageClasses = RepoBarMarker.class)
public class MongoConfiguration extends AbstractRepoConfig {
#Override
#Bean
public MongoOperations mongoTemplate() {
final MongoClient mongo = this.getMongoClient();
final MongoClientURI mongoUri = this.getMongoClientUri();
final MongoTemplate mongoTemplate = new MongoTemplate(mongo, mongoUri.getDatabase());
mongoTemplate.setReadPreference(ReadPreference.secondaryPreferred());
mongoTemplate.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
return mongoTemplate;
}
}
Question
If I change in foo repository the entity name to CarFoo.class and the repository to CarFooRepository.class, then everything works. But is there away to avoid renaming them and still have a real wiring per type, instead of name (as it is what seems to be done here), for Spring Data repositories?
In your case, you can use
#Repository("fooCarRepository")
on the interface declaration of
com.foo.repository.CarRepository
Although when using Spring Data #Repository is not generally needed on the interface, however in your case you need to supply it. That's because you need to make Spring register the implementation of the bean with a custom name (in this case fooCarRepository) in order to avoid the name collision.

Resources