I'm using ModelMapper the following way :
I have a few converter classes that are Spring components and they register custom ModelMapper mappings
@Component
public class FooConverter {
@Autowired
private ModelMapper modelMapper;
public static final PropertyMap<Foo, FooModel> FOO_TO_FOO_MODEL_MAP = new PropertyMap<Foo, FooModel>() {
@Override
protected void configure() {
map().setTimeZone(source.getTimeZone().getID());
}
};
@PostConstruct
public void init() {
modelMapper.addMappings(FOO_TO_FOO_MODEL_MAP);
}
}
But I get the following error when Spring starts because the configure function gets called and the source is null.
How is that supposed to work?
Am I using it wrong?
1 error
at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241)
at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:244)
at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:96)
at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:92)
at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:124)
at org.modelmapper.ModelMapper.addMappings(ModelMapper.java:113)
at com.agilitypr.neptune.account.api.v1.controllers.PreferenceController.getUserAccountPreferences(PreferenceController.java:63)
at com.agilitypr.neptune.account.api.v1.controllers.PreferenceController$$FastClassBySpringCGLIB$$3559fcbb.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.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
at com.agilitypr.neptune.account.api.v1.filters.AuthorizationFilter.doAuthorize(AuthorizationFilter.java:52)
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.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
at com.agilitypr.neptune.account.api.v1.filters.AuthenticationFilter.doAuthenticate(AuthenticationFilter.java:48)
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.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.agilitypr.neptune.account.api.v1.controllers.PreferenceController$$EnhancerBySpringCGLIB$$a4d3aeda.getUserAccountPreferences(<generated>)
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.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:865)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)
at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:215)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1317)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1219)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:762)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:680)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at org.modelmapper.internal.ExplicitMappingBuilder$ExplicitMappingInterceptor.access$000(ExplicitMappingBuilder.java:304)
at org.modelmapper.internal.ExplicitMappingBuilder.createAccessorProxies(ExplicitMappingBuilder.java:287)
at org.modelmapper.internal.ExplicitMappingBuilder.createProxies(ExplicitMappingBuilder.java:277)
at org.modelmapper.internal.ExplicitMappingBuilder.visitPropertyMap(ExplicitMappingBuilder.java:266)
at org.modelmapper.PropertyMap.configure(PropertyMap.java:386)
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.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:227)
... 90 more
The bean is declared like this and Foo and FooModel are not final
@Configuration
public class FooConfiguration {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
Important edit
Actually, even when I create the mapping explicitly in the function inside a main class, I get the same NullPointerException.
public class main {
public static void main(String[] args) {
ModelMapper mapper = new ModelMapper();
final PropertyMap<Foo, FooModel> FOO_TO_FOO_MODEL_MAP = new PropertyMap<Foo, FooModel>() {
@Override
protected void configure() {
map().setTimeZone(source.getTimeZone().getID());
}
};
mapper.addMappings(FOO_TO_FOO_MODEL_MAP );
}
}
What is wrong?
I am following the example in their documentation.
EDIT
Deep mapping is working with these
public class InnerFoo {
private int prop3;
public int getProp3() {
return prop3;
}
public void setProp3(int prop3) {
this.prop3 = prop3;
}
}
public class Foo {
private InnerFoo innerFoo;
public InnerFoo getInnerFoo() {
return innerFoo;
}
public void setInnerFoo(InnerFoo innerFoo) {
this.innerFoo = innerFoo;
}
}
public class FooModel {
private int prop2;
public int getProp2() {
return prop2;
}
public void setProp2(int prop2) {
this.prop2 = prop2;
}
}
the problem is only with the TimeZone object so I have to use a converter, which is not ideal.
map().setTimeZone(source.getTimeZone().getID());
public class Foo {
private TimeZone timeZone;
//Setters//Getters
}
public class FooModel {
private String timeZoneId;
//Setters//Getters
}
I think this is ultimately due to the fact that ModelMapper can not instantiate
TimeZone
objects (norLocalDateTime
etc.) at the time of configuring the mapper.Actually you don't have to configure anything.
This works for me. ModelMapper found out that you want to map the
TimeZone
's propertyID
toFooModel
's propertytimeZoneId
.Nevertheless, just in case you want to do that manually: Following the docs quickly, I found the concept of converters. Using a
Converter
which convertsTimeZone
toString
you can do this:For anyone coming across with the same issue: My problem was that I used PropertyMap with anonymous implementation like the documentation suggested within a spring configuration. This messed up within the ExplicitMappingBuilder. What I have now is the following:
My UserPropertyMap looks like this:
This worked like a charm in Spring Boot 2.
Autowire using constructor like
also make sure your ModelMapper bean has been defined in your spring config class like below: