orika-mapper/orika

Null pointer exception occurred after upgrading orika-mapper to latest [1.5.4] while mapping list of object by passing mappingContext

Opened this issue · 2 comments

@component
public class CustomMapper extends ConfigurableMapper implements Imapper {

@OverRide
public void configure(MapperFactory mapperFactory) {
mapperFactory.registerClassMap(mapperFactory.classMap(A.class,B.class)
.byDefault().customize(getAtoBMapper()).toClassMap());
}

private CustomMapper<A,B> getAtoBMapper() {
return new CustomMapper<A,B>() {
@OverRide
public void mapAtoB(A a,B b, MappingContext context) {

};

}

Here is caller:

MappingContext mappingContext = new MappingContext(map);
List bList =
customMapper.mapAsList(aList,B.class,
mappingContext);
//aList is a list of object a

Exception occurred:

ma.glasnost.orika.MappingException: java.lang.NullPointerException
at ma.glasnost.orika.impl.generator.MapperGenerator.build(MapperGenerator.java:104) ~[orika-core-1.5.4.jar:?]
at ma.glasnost.orika.impl.DefaultMapperFactory.buildMapper(DefaultMapperFactory.java:1478) ~[orika-core-1.5.4.jar:?]
at ma.glasnost.orika.impl.DefaultMapperFactory.lookupMapper(DefaultMapperFactory.java:738) ~[orika-core-1.5.4.jar:?]
at ma.glasnost.orika.impl.MapperFacadeImpl.resolveMapper(MapperFacadeImpl.java:568) ~[orika-core-1.5.4.jar:?]
at ma.glasnost.orika.impl.MapperFacadeImpl.resolveMappingStrategy(MapperFacadeImpl.java:178) ~[orika-core-1.5.4.jar:?]
at ma.glasnost.orika.impl.DefaultBoundMapperFacade$BoundStrategyCache.getStrategy(DefaultBoundMapperFacade.java:259) ~[orika-core-1.5.4.jar:?]
at ma.glasnost.orika.impl.DefaultBoundMapperFacade.mapReverse(DefaultBoundMapperFacade.java:152) ~[orika-core-1.5.4.jar:?]
at ma.glasnost.orika.generated.Orika_B_A_Mapp

this.shouldCaptureFieldContext = (Boolean)mappingContext.getProperty(Properties.CAPTURE_FIELD_CONTEXT); above line is throwing NPE as "mappingContext.getProperty(Properties.CAPTURE_FIELD_CONTEXT)" is giving null!

It is a trivial fix, can someone push it ?

[ma.glasnost.orika.impl.generator.SourceCodeContext] line 89:

  • this.shouldCaptureFieldContext = (Boolean) mappingContext.getProperty(Properties.CAPTURE_FIELD_CONTEXT);
  • this.shouldCaptureFieldContext = Boolean.valueOf(mappingContext.getProperty(Properties.CAPTURE_FIELD_CONTEXT));

Since there is no fix so far, here is a workaround.
We need to get a proper ma.glasnost.orika.MappingContext created by Orika itself, so mappingContext.getProperty(Properties.CAPTURE_FIELD_CONTEXT) is not null. To achieve this we have to create a utility class.

package org.test;

import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.MappingContextFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;

/**
 * The utility class to get {@link MappingContext} configured by Orika
 * and ready to be used by {@link ma.glasnost.orika.MapperFacade#map(java.lang.Object, java.lang.Class)}.
 */
public final class MappingContextUtils {

    private static final MappingContextFactory MAPPING_CONTEXT_FACTORY;

    static {
        MAPPING_CONTEXT_FACTORY = new CustomDefaultMapperFactory.Builder().build().getContextFactory();
    }

    /**
     * Gets {@link MappingContext} ready to be used by {@link ma.glasnost.orika.MapperFacade#map(java.lang.Object, java.lang.Class)}.
     * Manual creation of {@link MappingContext} does not work. Please see the link below.
     *
     * @return {@link MappingContext}
     * @see <a href="https://github.com/orika-mapper/orika/issues/354">https://github.com/orika-mapper/orika/issues/354</a>
     */
    public static MappingContext getMappingContext() {
        return MAPPING_CONTEXT_FACTORY.getContext();
    }

    /**
     * The custom {@link DefaultMapperFactory} to expose getter
     * for {@link MappingContextFactory} so {@link MappingContext} with prefilled globalProperties can be created.
     *
     * @author Dmitry Lebedko (dmitry.lebedko@t-systems.com)
     */
    private static class CustomDefaultMapperFactory extends DefaultMapperFactory {

        /**
         * Constructs a new instance of DefaultMapperFactory
         *
         * @param builder {@link MapperFactoryBuilder}
         */
        protected CustomDefaultMapperFactory(MapperFactoryBuilder<?, ?> builder) {
            super(builder);
        }

        /**
         * Gets {@link MappingContextFactory}.
         *
         * @return {@link MappingContextFactory}
         */
        public MappingContextFactory getContextFactory() {
            return contextFactory;
        }

        public static class Builder extends DefaultMapperFactory.MapperFactoryBuilder<CustomDefaultMapperFactory, Builder> {

            @Override
            public CustomDefaultMapperFactory build() {
                return new CustomDefaultMapperFactory(this);
            }

            @Override
            protected CustomDefaultMapperFactory.Builder self() {
                return this;
            }

        }

    }

}

Now it may be used like that

MappingContext mappingContext = MappingContextUtils.getMappingContext(); // here we get a mapping context created by Orika
List bList = customMapper.mapAsList(aList, B.class, mappingContext);