Spring MongoDB - org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class - spring

I am evaluating MongoDB and Spring Data MongoDB in a spring boot application for one of our upcoming project. I have a simple POJO which would be saved in MongoDB
#Document(collection = "customers")
public class CustomerDoc{
#org.springframework.data.annotation.Id
private String Id;
private String firstName;
private String lastName;
private MonetaryAmount amount;
public CustomerDoc(String firstName, String lastName, MonetaryAmount amount){
this.firstName = firstName;
this.lastName = lastName;
this.amount = amount;
}
}
The Spring configuration is
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
MongoClient mongoClient = new MongoClient("localhost", 27017);
return new SimpleMongoDbFactory(mongoClient, mongoDbName);
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mongoConverter());
}
#Bean
public MappingMongoConverter mongoConverter() throws Exception {
MongoMappingContext mappingContext = new MongoMappingContext();
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory());
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mappingContext);
mongoConverter.setMapKeyDotReplacement("\\:");
return mongoConverter;
}
In the configuration, I have to set the replacement key because without having it, I get exception:
org.springframework.data.mapping.model.MappingException: Map key java.lang.Class contains dots but no replacement was configured! Make sure map keys don't contain dots in the first place or configure an appropriate replacement!
This happens because of the MonetaryAmount object in the Customer class
The customer repository is :
public interface CustomerDocRepository extends MongoRepository<CustomerDoc, String>{
public List<CustomerDoc> findByFirstName();
}
Now if I try to save a customer object, I get this exception:
Exception in thread "main" java.lang.IllegalStateException: Failed to execute CommandLineRunner
at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:675)
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:690)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
at com.poc.mongodb.MongoDbApp.main(MongoDbApp.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.lang.Class.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:210)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:128)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:61)
at com.mongodb.CompoundDBObjectCodec.encode(CompoundDBObjectCodec.java:48)
at com.mongodb.CompoundDBObjectCodec.encode(CompoundDBObjectCodec.java:27)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
at com.mongodb.connection.InsertCommandMessage.writeTheWrites(InsertCommandMessage.java:101)
at com.mongodb.connection.InsertCommandMessage.writeTheWrites(InsertCommandMessage.java:43)
at com.mongodb.connection.BaseWriteCommandMessage.encodeMessageBodyWithMetadata(BaseWriteCommandMessage.java:129)
at com.mongodb.connection.RequestMessage.encodeWithMetadata(RequestMessage.java:160)
at com.mongodb.connection.WriteCommandProtocol.sendMessage(WriteCommandProtocol.java:212)
at com.mongodb.connection.WriteCommandProtocol.execute(WriteCommandProtocol.java:101)
at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:67)
at com.mongodb.connection.InsertCommandProtocol.execute(InsertCommandProtocol.java:37)
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:159)
at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:286)
at com.mongodb.connection.DefaultServerConnection.insertCommand(DefaultServerConnection.java:115)
at com.mongodb.operation.InsertOperation.executeCommandProtocol(InsertOperation.java:76)
at com.mongodb.operation.BaseWriteOperation$1.call(BaseWriteOperation.java:141)
at com.mongodb.operation.BaseWriteOperation$1.call(BaseWriteOperation.java:133)
at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:230)
at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:221)
at com.mongodb.operation.BaseWriteOperation.execute(BaseWriteOperation.java:133)
at com.mongodb.operation.BaseWriteOperation.execute(BaseWriteOperation.java:60)
at com.mongodb.Mongo.execute(Mongo.java:781)
at com.mongodb.Mongo$2.execute(Mongo.java:764)
at com.mongodb.DBCollection.executeWriteOperation(DBCollection.java:333)
at com.mongodb.DBCollection.insert(DBCollection.java:328)
at com.mongodb.DBCollection.insert(DBCollection.java:319)
at com.mongodb.DBCollection.insert(DBCollection.java:289)
at com.mongodb.DBCollection.insert(DBCollection.java:255)
at com.mongodb.DBCollection.insert(DBCollection.java:239)
at org.springframework.data.mongodb.core.MongoTemplate$10.doInCollection(MongoTemplate.java:1054)
at org.springframework.data.mongodb.core.MongoTemplate$10.doInCollection(MongoTemplate.java:1049)
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:462)
at org.springframework.data.mongodb.core.MongoTemplate.insertDBObjectList(MongoTemplate.java:1049)
at org.springframework.data.mongodb.core.MongoTemplate.doInsertBatch(MongoTemplate.java:935)
I am using mongo driver 3.2.2 Spring data MongoDB 1.9.1
Any help would be really appreciated

Related

InvalidFormatException for Date - fixing without using JsonFormat or modifying original class

Introduction
We are using a custom starter hosted on a nexus repository, that contains spring-cloud-feign clients that make requests to microservices.
One of the microservices returns the dates as "dd-MM-yyyy HH:mm:ssZ" and this works in most of our applications. However, we have one application that is throwing the following error:
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2019-10-16 14:23:17": not a valid representation (error: Failed to parse Date value '2019-10-16 14:23:17': Unparseable date: "2019-10-16 14:23:1
7")
Current work-around
My Current work-around, as I don't want to pollute the starter, is to extend the class and create a local feign-client and local pojo with the proper JsonFormat:
public class DocumentMetaDataFix extends DocumentMetaData {
#JsonFormat(
shape = Shape.STRING,
pattern = "yyyy-MM-dd HH:mm:ss"
)
private Date creationDate;
#JsonFormat(
shape = Shape.STRING,
pattern = "yyyy-MM-dd HH:mm:ss"
)
Failed Fixes
I have tried the following in my configuration class, in order to try affecting the de-serialization from another path. However, the DocumentMetaDataSerializer is never called. The ObjectMapper bean IS called.
#Configuration
#EnableSpringDataWebSupport
#RequiredArgsConstructor
public class MyConfig extends WebMvcConfigurerAdapter {
#Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization() {
return new Jackson2ObjectMapperBuilderCustomizer() {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.deserializerByType(DocumentMetaData.class, new DocumentMetaDataDeserializer());
}
};
}
#Primary
#Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
mapper.setDateFormat(new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"));
//mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
return mapper;
}
#Bean
public Module dynamoDemoEntityDeserializer() {
SimpleModule module = new SimpleModule();
module.addDeserializer(DocumentMetaData.class, new DocumentMetaDataDeserializer());
return module;
}
public static class DocumentMetaDataDeserializer extends JsonDeserializer<DocumentMetaData> {
#Override
public DocumentMetaData deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// return DynamoDemoEntity instance;
JsonNode node = jp.getCodec().readTree(jp);
return null;
}
public DocumentMetaData deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer t) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return null;
}
}
Full Stacktrace
Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2019-10-16 14:23:17": not a valid representation (error: Failed to parse Date value '2019-10-16 14:23:17': Unparseable date: "2019-10-16 14:23:1
7")
at [Source: (ByteArrayInputStream); line: 1, column: 580] (through reference chain: eu.europa.ec.nova.documentstore.DocumentMetaData["creationDate"])
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1548)
at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:910)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:524)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseDate(StdDeserializer.java:467)
at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateBasedDeserializer._parseDate(DateDeserializers.java:195)
at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:285)
at com.fasterxml.jackson.databind.deser.std.DateDeserializers$DateDeserializer.deserialize(DateDeserializers.java:268)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:237)
... 70 common frames omitted
So, any ideas?
I have searched through the project for references to Jackson in case there is anything else in my project causing this.
I am will try to go inside the ObjectMapper and try to debug the current parameters/fields of the configuration at ObjectMapper.java:3084 from the stacktace:
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:237)
... 67 common frames omitted
Update
I added a breakpoint in the objectmapper constructor, and am seeing that it is being initialized from more than one location. This led me to suspect that spring-boot is not using my ObjectMapper. Instead it is using an internal spring one that is called from MappingJackson2HttpMessageConverter .
<init>:480, ObjectMapper
build:606, Jackson2ObjectMapperBuilder
<init>:59, MappingJackson2HttpMessageConverter
<init>:74, AllEncompassingFormHttpMessageConverter
I will therefore try to over-ride this internal spring one, based on results I found from: How to customise the Jackson JSON mapper implicitly used by Spring Boot?
However this also failed.
References
Is it possible to configure Jackson custom deserializers at class level for different data types?
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper
https://www.baeldung.com/jackson-deserialization
very useful: https://mostafa-asg.github.io/post/customize-json-xml-spring-mvc-output/
How to customise Jackson in Spring Boot 1.4
Update - final list of tries
It still fails with an error.
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
builder.serializationInclusion(Include.NON_EMPTY);
builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
//converters.add(cmsaMessageConverter());
converters.add(new StringHttpMessageConverter());
converters.add(new FormHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter());
}
#Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization() {
return new Jackson2ObjectMapperBuilderCustomizer() {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.deserializerByType(DocumentMetaData.class, new DocumentMetaDataDeserializer());
}
};
}
#Primary
#Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
mapper.setDateFormat(new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"));
//mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
return mapper;
}
#Bean
public Module dynamoDemoEntityDeserializer() {
SimpleModule module = new SimpleModule();
module.addDeserializer(DocumentMetaData.class, new DocumentMetaDataDeserializer());
return module;
}
public static class DocumentMetaDataDeserializer extends JsonDeserializer<DocumentMetaData> {
#Override
public DocumentMetaData deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// return DynamoDemoEntity instance;
JsonNode node = jp.getCodec().readTree(jp);
return null;
}
public DocumentMetaData deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer t) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
return null;
}
}
It still fails with an error.
Try using LocalDateTime,
this is what I'm doing and working for me
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime date;

Unable to load application properties in springboot

I have application related properties in my spring boot application. The strange issue is sometimes the properties load without any issues but most of the times they throw an exception.
Here is my spring boot class annotated with #Configuration. Tried debugging the issue but could not find any reason for this weird behavior.
#Configuration
public class RedisConfig {
private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);
#Value("${redisurl}")
private String redisURL;
#Value("${redisport}")
private String redisPort;
#Bean
public JedisConnectionFactory redisConnectionFactory() {
logger.info("--redisURL-" + redisURL);
logger.info("--redisPort-" + redisPort);
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(redisURL);
redisConnectionFactory.setPort(Integer.parseInt(redisPort));
logger.info("--connected to redis--");
return redisConnectionFactory;
}
redisurl=cst-prd-007
redisport=6379
redispassword=
Any help is appreciated.
Stacktrace:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.redis.RedisHealthIndicatorConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'redisurl' in value "${redisurl}"
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:729)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:192)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1270)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'redisurl' in value "${redisurl}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:237)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:211)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:834)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1086)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91)
at org.springframework.beans.factory.annotation.AutowiredAnnotationB
Edit:
I also tried doing this. Did not work
#Autowired
Environment env;
#Bean
public JedisConnectionFactory redisConnectionFactory() {
logger.info("--redisURL-" + redisURL);
logger.info("--redisPort-" + redisPort);
redisURL = env.getRequiredProperty("redis.url");
redisPort = env.getRequiredProperty("redis.port");
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(redisURL);
redisConnectionFactory.setPort(Integer.parseInt(redisPort));
logger.info("--connected to redis--");
return redisConnectionFactory;
}
Issue solved after using the below approach
#Component
public class RedisConf {
#Value("${redis.url}")
String url;
#Value("${redis.port}")
int port;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
Instead of using #Value annotation, you can try using Environment from Spring Framework API.
Like this,
#Configuration
public class RedisConfig {
private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);
private String redisURL, redisPort;
#Autowired
private Environment env;
#Bean
public JedisConnectionFactory redisConnectionFactory() {
redisURL = env.getRequiredProperty("redisurl");
redisPort = env.getRequiredProperty("redisport");
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName(redisURL);
redisConnectionFactory.setPort(Integer.parseInt(redisPort));
logger.info("--connected to redis--");
return redisConnectionFactory;
}
The issue is solved after using the below approach
#Component
public class RedisConf {
#Value("${redis.url}")
String url;
#Value("${redis.port}")
int port;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}

Introducing Spring Batch fails Dao-Test

I am using Spring Batch in a Spring Boot application as below. Spring Batch and the application seem to work fine with that configuration.
However, with that configuration for a simple Dao-Test (not for Spring Batch) I get the following exception. Without the Spring Batch configuration the test is running fine. Update: The problem appears if I configure to use an own JobRepository with a transaction manager (below in class MyBatchConfigurer).
I tried to provide another transaction manager for Spring Batch but I am running from exception to exception.
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3505)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1427)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1423)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350)
at com.sun.proxy.$Proxy165.flush(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:305)
at com.sun.proxy.$Proxy165.flush(Unknown Source)
at com.foo.dao.GenericDao.save(GenericDao.java:60)
at com.foo.dao.GenericDao$$FastClassBySpringCGLIB$$71a0996b.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
... 37 more
Test Setup
#SpringBootTest
#RunWith(SpringRunner.class)
#EntityScan(basePackages = "com.foo.entity")
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#TestPropertySource("/mytest.properties")
#Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:/testdata.sql")
public class MyTest {
#Inject
private OrderDao sut;
#Test
public void test_findByAnotherFieldId() {
final Order order = // build an order ...
sut.save(order);
final Order result = sut.findByAnotherFieldId("valueOfOtherField");
assertThat(result).isEqualTo(order);
}
}
Spring Batch Job configuration
#Configuration
#EnableBatchProcessing
#Import(OracleDatabaseConfig.class)
#ComponentScan(basePackageClasses = MyBatchConfigurer.class)
public class BatchJobConfig {
// Injects...
#Qualifier("analyseOrdersJob")
#Bean
public Job analyseOrdersJob() {
return jobBuilderFactory.get("analyseOrdersJob").start(analyseOrdersStep()).build();
}
#Bean
public Step analyseOrdersStep() {
return stepBuilderFactory.get("analyseOrdersStep").<Order, String>chunk(4)
.reader(orderItemReader) //
.processor(orderItemProcessor) //
.writer(orderItemWriter) //
.build();
}
}
Spring Batch configuration
#Component
public class MyBatchConfigurer extends DefaultBatchConfigurer {
#Inject
private DataSource dataSource;
#Override
public JobRepository getJobRepository() {
try {
return extractJobRepository();
} catch (final Exception e) {
throw new BatchConfigurationException(e);
}
}
private JobRepository extractJobRepository() throws Exception {
final JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(getTransactionManager());
factory.afterPropertiesSet();
return factory.getObject();
}
}
ItemReader
#Component
#StepScope
public class OrderItemReader implements ItemReader<Order> {
#Inject
private OrderDao orderdao;
private int nextOrderIndex;
private List<Order> orders;
#PostConstruct
public void postConstruct() {
orders = orderdao.findAll();
}
#Override
public Order read() {
if (nextOrderIndex < orders.size()) {
final Order order = orders.get(nextOrderIndex);
nextOrderIndex++;
return order;
}
return null;
}
}
ItemWriter and ItemProcessor are similarly configured.

Using Spring Scheduler to return rows from DB throws Null pointer Exception

I am using Spring scheduling to schedule a task that performs some DB operation every hour. This throws a NullPointerException every time I try to call a function that triggers DB specific operations.
Configuration File :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages="com.bt.rtddashboard")
/*#EnableJpaRepositories(basePackages = {
"com.bt.rtddashboard"
})*/
#EnableScheduling
#PropertySource("classpath:jdbc.properties")
public class RTDDashboardConfiguration extends WebMvcConfigurerAdapter {
#Resource
private Environment env;
#Bean(name = "dataSource", destroyMethod="close")
public DataSource getDataSource() {
//DB code
}
private Properties getHibernateProperties() {
//Hibernate code
}
#Bean(name = "sessionFactory")
#Scope("singleton")
public LocalSessionFactoryBean getSessionFactory() {
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getHibernateTransactionManager(SessionFactory factory) {
;
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/").setCachePeriod(31556926);
}
#Scheduled(fixedRate=1000)
public void work() {
// task execution logic
System.out.println("Scheduled Task");
DashboardController controller=new DashboardController();
controller.performTask();
}
}
Dashboard Controller :
#RestController
public class DashboardController {
#Autowired
private InlineService service;
#RequestMapping(value="/rtd/getAllServers", method=RequestMethod.GET)
public ResponseEntity<List<ServerMonitor>> getAllServers(){
List<ServerMonitor> ilsList=new ArrayList<ServerMonitor>();
ResponseEntity<List<ServerMonitor>> response=null;
try{
ilsList=service.getAllServers();
response=new ResponseEntity<List<ServerMonitor>>(ilsList,HttpStatus.OK);
System.out.println("Scheduled Time");
}catch(Exception e){
}
return response;
}
public void performTask(){
System.out.println("Scheduled Task Starts in Contrroller");
List<IlsStats> ilsStats =new ArrayList<IlsStats>();
List<IlsStatsHistory> ilsStatsHisList=new ArrayList<IlsStatsHistory>();
try{
//get All the ILS Stats
ilsStats=service.getAllInlineStats();
System.out.println("No of rows to Copy : " + ilsStats.size());
ilsStatsHisList=formILSHistoryList(ilsStats);
int flag=service.insertInIlsStatsHistory(ilsStatsHisList);
//List<ServerMonitor> ilsList=service.getAllServers();
}catch(Exception e){
e.printStackTrace();
}
}
Here, ilsStats=service.getAllInlineStats(); throws NullPointerException.
Even though the rest webservice created on top of it is working fine.
Stack Trace :
Scheduled Task
Scheduled Task Starts in Contrroller
java.lang.NullPointerException
at com.bt.rtddashboard.controller.DashboardController.performTask(DashboardController.java:226)
at com.bt.rtddashboard.configuration.RTDDashboardConfiguration.work(RTDDashboardConfiguration.java:137)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
The same stack trace comes up every 1 sec now.
Creating an #Scheduled method in a configuration class is a very bad idea (imho). Next to that I also think that having a scheduler calling a controller method (a method tied to a web related component!) is generally a bad idea/design.
Nonetheless the problem is that you do new DashboardController(). That will obviously create a bean outside the scope of spring and will never be dependency injected. Instead inject the into the class and use that instance.
#Autowired
private DashboardController controller;
#Scheduled(fixedRate=1000)
public void work() {
controller.performTask();
}
Or even better just remove that method all together and simply place #Scheduled(fixedRate=1000) on the performTask method instead.

MongoDB and Spring JPA integration is throwing error

I am trying to integrate Spring JPA with MongoDB. My intention is to just retrieve data from mongo DB. I am getting the below error while injecting my repository.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mongoService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.org.coop.society.data.mongo.repositories.MaterialMasterRepository com.org.coop.security.service.MongoService.materialMasterRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'materialMasterRepository': Invocation of init method failed; nested exception is java.lang.AbstractMethodError
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
My configuration snippet is given below.
TestMongoDBConfig.java
#Configuration
#EnableMongoRepositories(basePackages = {"com.abc.data.mongo.repositories"})
#ComponentScan(basePackages = "com.abc")
#PropertySource("classpath:applicationTest.properties")
public class TestMongoDBConfig extends AbstractMongoConfiguration {
#Autowired
private Environment env;
#Override
protected String getDatabaseName() {
return "retail";
}
#Override
#Bean
public MongoClient mongo() throws Exception {
MongoClient client = new MongoClient("localhost", 27017);
return client;
}
#Override
protected String getMappingBasePackage() {
return "com.abc.data.mongo.entities";
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongo(), getDatabaseName());
}
}
MaterialMaster.java in com.abc.data.mongo.entities package
#Document(collection = "materialMaster")
public class MaterialMaster {
#Id
private Long materialId;
#Field
private String name;
MaterialMasterRepository.java in com.abc.data.mongo.repositories package
public interface MaterialMasterRepository extends MongoRepository<MaterialMaster, Long> {
}
MongoService.java in com.abc.service package
#Service
public class MongoService {
#Autowired
private MaterialMasterRepository materialMasterRepository;
public void getMaterials() {
List<MaterialMaster> materials = materialMasterRepository.findAll();
System.out.println(materials);
}
}
Junit class looks like below
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan(basePackages = "com.abc")
#ContextHierarchy({
#ContextConfiguration(classes={TestMongoDBConfig.class})
})
public class ModuleWSTest {
#Autowired
private MongoService mongoService;
#Test
public void testModule() {
mongoService.getMaterials();
}
}
I have tried all possible changes (as per my knowledge) but no luck. Any help is really appreciated.
The error message is little confusing. I was using latest spring-data-mongodb (1.8.4.RELEASE). Once I downgraded the dependency to 1.6.0.RELEASE then the above configuration started working.

Resources