spring-data-mongodb
spring-data-mongodb copied to clipboard
Document with LocalDateTime field doesn't work in Java 17
Migrated very simple java application from Java 11 to Java 17. Have singe Document
@Document
@Data
@NoArgsConstructor
public class SyncTask {
@Id
private ObjectId id;
@Indexed
private String taskId;
private LocalDateTime startedOn;
}
And getting issue on start up
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make private java.time.LocalDateTime(java.time.LocalDate,java.time.LocalTime) accessible: module java.base does not "opens java.time" to unnamed module @757acd7b
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Constructor.checkCanSetAccessible(Constructor.java:188)
at java.base/java.lang.reflect.Constructor.setAccessible(Constructor.java:181)
at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:202)
at org.springframework.data.mapping.PreferredConstructor.<init>(PreferredConstructor.java:53)
at org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers.buildPreferredConstructor(PreferredConstructorDiscoverer.java:224)
at org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers.access$200(PreferredConstructorDiscoverer.java:93)
at org.springframework.data.mapping.model.PreferredConstructorDiscoverer$Discoverers$1.discover(PreferredConstructorDiscoverer.java:136)
at org.springframework.data.mapping.model.PreferredConstructorDiscoverer.discover(PreferredConstructorDiscoverer.java:81)
at org.springframework.data.mapping.model.InstanceCreatorMetadataDiscoverer.discover(InstanceCreatorMetadataDiscoverer.java:83)
at org.springframework.data.mapping.model.BasicPersistentEntity.<init>(BasicPersistentEntity.java:113)
at org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity.<init>(BasicMongoPersistentEntity.java:84)
at org.springframework.data.mongodb.core.mapping.MongoMappingContext.createPersistentEntity(MongoMappingContext.java:100)
at org.springframework.data.mongodb.core.mapping.MongoMappingContext.createPersistentEntity(MongoMappingContext.java:41)
at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:430)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:406)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.lambda$createAndRegisterProperty$3(AbstractMappingContext.java:630)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.createAndRegisterProperty(AbstractMappingContext.java:627)
at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:581)
at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:714)
at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:449)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:406)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:291)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:209)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:92)
at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:74)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getEntityInformation(MongoRepositoryFactory.java:166)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getTargetRepository(MongoRepositoryFactory.java:140)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:325)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:323)
at org.springframework.data.util.Lazy.getNullable(Lazy.java:231)
at org.springframework.data.util.Lazy.get(Lazy.java:115)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:329)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean.afterPropertiesSet(MongoRepositoryFactoryBean.java:119)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
... 44 common frames omitted
Obviously related to this change - https://openjdk.org/jeps/396 If I remove LocalDateTime field - it starts without issue.
I might be missing something here. A fresh boot 3.0.5 project with the above entity works just fine on openjdk version "17.0.4.1".
Maybe you can take the time to provide a complete minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem.
Hi @christophstrobl , I'm using boot 3.0.5 project with java version 17.0.6.When I'm trying to start the service I'm getting above mentioned exception. I'm using two mongo Db and did the configuration for that and using custom converters. It was working fine in java 11 but after upgrading it to 17, I started facing above exception.
@vikas-singh-maersk do you have a minimal sample (something that we can unzip or git clone, build, and deploy) that reproduces the problem at hand?
Hi @christophstrobl , I have to create the minimal sample to share with you. But I have found the cause of issue. As I mentioned earlier I'm using 2 mongo DB Connection in my project and for that I'm using @EnableMongoRepositories annotation. This annotation is creating problem to access Localdatetime. If I'll remove this annotation my code works fine. It seems like @EnableMongoRepositories annotation internally uses the MongoRepositoriesRegistrar class, which tries to create a MappingContext instance using the LocalDateTime class. Can you look into it ?
Hi, @christophstrobl
I also faced the kind of issue reported in this ticket, when trying to update a project to Spring Boot 3+.
It is likely due to the use of ReflectionUtils.makeAccessible from spring-core, that uses setAccessible(true) internally.
Background: https://mail.openjdk.org/pipermail/jdk9-dev/2016-November/005276.html
Here is a minimal sample to reproduce the crash: https://github.com/christophe-michard/MongoSetAccessibleIssue.
Important part:
@SpringBootApplication
public class MongoSetAccessibleIssueApplication {
public static void main(String[] args) {
SpringApplication.run(MongoSetAccessibleIssueApplication.class, args);
}
public MongoSetAccessibleIssueApplication() {
// Create a custom mongoTemplate
// Crash happens only if we create a template with the mappingMongoConverter as second argument
MongoDatabaseFactory mongoDbFactory = new SimpleMongoClientDatabaseFactory(MongoClients.create(), "db");
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), new MongoMappingContext());
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, mappingMongoConverter);
// Crash happens when this line is executed
mongoTemplate.save(new MongoEntity());
}
}
class MongoEntity {
// We need a field that is a class from java.time package, for instance Instant, to provoke a crash.
// Background: https://mail.openjdk.org/pipermail/jdk9-dev/2016-November/005276.html
// Crash sample:
// Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make private java.time.Instant(long,int) accessible: module java.base does not "opens java.time" to unnamed module @1afd44cb
// at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
// at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
// at java.base/java.lang.reflect.Constructor.checkCanSetAccessible(Constructor.java:188) ~[na:na]
// at java.base/java.lang.reflect.Constructor.setAccessible(Constructor.java:181) ~[na:na]
// at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:201) ~[spring-core-6.0.9.jar:6.0.9]
// at org.springframework.data.mapping.PreferredConstructor.<init>(PreferredConstructor.java:56) ~[spring-data-commons-3.1.0.jar:3.1.0]
public Instant createdDateTime;
}
Is it enough for you to be able to investigate on the issue? 😃
@christophe-michard thanks for the reproducer!
@christophstrobl We are using this kind of configuration to remove the autogenerated _class field, and automatically create indexes on application start. Do you know if there is a way to do that without hitting the bug? It is currently a blocker for us to update to Java 17 and Spring Boot 3 (whose version 2.7 is EOL in November).
I need to revisit StoreConversions and the flagging for java.time types which may take some time.
Meanwhile there's always the option of adding --add-opens=java.base/java.time=ALL-UNNAMED.
@christophe-michard you may also want to try it this way:
MongoMappingContext mappingContext = new MongoMappingContext();
MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), mappingContext);
mappingContext.setSimpleTypeHolder(mappingMongoConverter.getCustomConversions().getSimpleTypeHolder());
mappingContext.afterPropertiesSet();
mappingMongoConverter.afterPropertiesSet();
@christophstrobl Thank you very much! 😄
With the lines necessary to generate the indexes and remove the _class field inserted before the afterPropertiesSet() lines (not sure of the right timing), the application passed successfully the Mongo initialization.
mappingContext.setAutoIndexCreation(true);
mappingMongoConverter.setTypeMapper(new DefaultMongoTypeMapper(null));
Now, the application stops at Solr initialization, so I will need to update solr-solrj library, and replace spring-data-solr by a custom bootstrap I guess, since it won't support Spring Boot 3 due to end of life reached. Only after I finished that, I may be able to say if the temporary fix above is fully working, by running our test suite.
We have the same issue
- OffsetDateTime
- LocalDate
hence all java.time.* classes
@christophe-michard you may also want to try it this way:
MongoMappingContext mappingContext = new MongoMappingContext(); MappingMongoConverter mappingMongoConverter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory), mappingContext); mappingContext.setSimpleTypeHolder(mappingMongoConverter.getCustomConversions().getSimpleTypeHolder()); mappingContext.afterPropertiesSet(); mappingMongoConverter.afterPropertiesSet();
@christophstrobl's snippet did resolve my problem.