commercetools / commercetools-sync-java

Java library for importing and syncing (taking care of changes) data into one or more commercetools projects from external data files or from another commercetools project.
https://commercetools.github.io/commercetools-sync-java
Apache License 2.0
32 stars 37 forks source link

Serialization error in UnresolvedReferencesServiceImpl #1138

Closed valtechtmn closed 8 months ago

valtechtmn commented 8 months ago

I'm currently using a very straightforward implementation of the product sync. It crashes out with a massive exception :

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.time.ZoneOffset java.time.ZonedDateTime.offset accessible: module java.base does not "opens java.time" to unnamed module @724af044
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:340) ~[?:?]
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:280) ~[?:?]
    at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176) ~[?:?]
    at java.base/java.lang.reflect.Field.setAccessible(Field.java:170) ~[?:?]
    at com.fasterxml.jackson.databind.util.ClassUtil.checkAndFixAccess(ClassUtil.java:939) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.fixAccess(FieldProperty.java:104) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder._fixAccess(BeanDeserializerBuilder.java:494) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder.build(BeanDeserializerBuilder.java:350) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:252) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:143) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:411) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:466) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:473) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:466) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:473) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:466) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:473) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:466) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:473) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:443) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:183) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.createContextual(CollectionDeserializer.java:27) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.handlePrimaryContextualization(DeserializationContext.java:650) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:484) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:466) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:473) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer(DeserializationContext.java:466) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:473) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:476) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4389) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3915) ~[java-invoke-local-all.jar:?]
    at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3853) ~[java-invoke-local-all.jar:?]
    at com.commercetools.sync.services.impl.UnresolvedReferencesServiceImpl.lambda$save$5(UnresolvedReferencesServiceImpl.java:128) ~[commercetools-sync-1.0-SNAPSHOT.jar:?]
    at java.base/java.util.Optional.map(Optional.java:265) ~[?:?]
    at com.commercetools.sync.services.impl.UnresolvedReferencesServiceImpl.lambda$save$6(UnresolvedReferencesServiceImpl.java:128) ~[commercetools-sync-1.0-SNAPSHOT.jar:?]
    at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:930) ~[?:?]
    ... 60 more

From what I gather researching the issue it's because Jackson is not properly configured to serialize DateTime. I've checked the source code of your project and it looks like everywhere when you use ObjectMapper you use

JsonUtils.getConfiguredObjectMapper()

Which comes fully configured for serializing dates as we can see from the commercetools java sdk v2

public static ObjectMapper createObjectMapper(final ModuleOptions options) {
        ServiceLoader<SimpleModule> loader = ServiceLoader.load(SimpleModule.class,
            SimpleModule.class.getClassLoader());

        ServiceLoader<ModuleSupplier> suppliers = ServiceLoader.load(ModuleSupplier.class,
            ModuleSupplier.class.getClassLoader());
        final List<SimpleModule> moduleList = new ArrayList<>();
        suppliers.iterator().forEachRemaining(moduleSupplier -> moduleList.add(moduleSupplier.getModule(options)));

        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule()) //provides serialization and deserialization for LocalDate and LocalTime (JSR310 Jackson module)
                .registerModule(new ZonedDateTimeSerializationModule()) //custom serializer for LocalDate, LocalTime and ZonedDateTime
                .registerModule(new ZonedDateTimeDeserializationModule()) //custom deserializer for ZonedDateTime
                .registerModule(new LocalDateDeserializationModule()) //custom deserializer for LocalDate
                .registerModules(loader)
                .registerModules(moduleList)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL) //ignore null fields
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        return objectMapper;
    }

Except in the UnresolvedReferencesServiceImpl where you instantiate the ObjectMapper directly

private static final ObjectMapper OBJECT_MAPPER =
      new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

And this is why I suspect I'm having this crash. Unless there is a reason for this specific configuration rather than using the full fledged one then I would propose to change this line to use the JsonUtils one.

Please let me know if you have other ideas or if you disagree with my analysis. I can make a PR if that's helpful.

lojzatran commented 8 months ago

Hi @valtechtmn

Thank you for reporting the error. I made a fix and released it.

valtechtmn commented 8 months ago

Thanks a lot I really appreciate it !