7

I'm having a weird ClassCastException while mapping an entity to a DTO with Orika in a sample Spring Boot webapp I'm working on. I get the exception when I attempt to do the mapping on the deployed app in embedded Tomcat, but I can do the mapping just fine in a JUnit test context. This are the relevant classes (they are all very simple):

JPA entity:

@Entity
public class Position {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    // getters/setters...
}

DTO:

public class PositionDto {

    private Integer id;
    private String name;
    // getters/setters...
}

Rest controller:

@RestController
public class PositionController {

    @Autowired
    private PositionService positionService;

    @RequestMapping("/position")
    public PositionDto get() {
        final PositionDto positionDto = positionService.getPosition(1);
        return positionDto;
    }
}

Service class:

@Service
public class PositionServiceImpl implements PositionService {

    @Autowired
    private PositionRepository positionRepository;
    @Autowired
    private OrikaBeanMapper mapper;

    @Transactional(readOnly = true)
    @Override
    public PositionDto getPosition(final Position.ID id) {
        // This returns a populated Position object with id=1 and name = "Creator"
        final Position position = positionRepository.findOne(id.getId());
        // This is where the mapping occurs
        return mapper.map(position, PositionDto.class);
    }
}

OrikaBeanMapper class:

@Component
public class OrikaBeanMapper extends ConfigurableMapper implements ApplicationContextAware {        

    public OrikaBeanMapper() {
        super(false);
    }

    @Override
    protected void configureFactoryBuilder(final   DefaultMapperFactory.Builder factoryBuilder) {
        factoryBuilder.mapNulls(false);
    }
    // Omitted non-important methods

}

And this is the stacktrace of the ClassCastException:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is ma.glasnost.orika.MappingException: While attempting the following mapping:
sourceClass = class com.dlizarra.startuphub.position.Position
destinationType = com.dlizarra.startuphub.position.PositionDto
resolvedStrategy = InstantiateAndUseCustomMapperStrategy<Position, PositionDto> {customMapper: GeneratedMapper<Position, PositionDto> {usedConverters: [], usedMappers: [], usedMapperFacades: [], usedTypes: [] }, unenhancer: ma.glasnost.orika.unenhance.BaseUnenhancer@73c3e10e, objectFactory: DefaultConstructorObjectFactory<PositionDto>}
Error occurred: java.lang.ClassCastException: com.dlizarra.startuphub.position.Position cannot be cast to com.dlizarra.startuphub.position.Position
-----begin dump of current state-----------------------------
Registered object factories: 1 (approximate size: 110.8 kB)
    [PositionDto] : {Position=DefaultConstructorObjectFactory<PositionDto>}
-------------------------------------------------------------------------------
Registered mappers: 1 (approximate size: 17,643.0 kB)
    [0] : GeneratedMapper<Position, PositionDto> {usedConverters: [], usedMappers: [], usedMapperFacades: [], usedTypes: [] }
-------------------------------------------------------------------------------
Registered concrete types: 5 (approximate size: 294.3 kB)
  [interface java.util.List] : ArrayList<Object>
  [interface java.util.Set] : LinkedHashSet<Object>
  [interface java.util.Collection] : ArrayList<Object>
  [interface java.util.Map] : LinkedHashMap<Object, Object>
  [interface java.util.Map$Entry] : MapEntry<Object, Object>
  -------------------------------------------------------------------------------

Resolved strategies: 1 (approximate size: 19,850.8 kB)

{source: Position, dest: PositionDto, in-place:false}:  InstantiateAndUseCustomMapperStrategy<Position, PositionDto>
{customMapper: GeneratedMapper<Position, PositionDto> {usedConverters: [],    usedMappers: [], usedMapperFacades: [], usedTypes: [] }, unenhancer:
ma.glasnost.orika.unenhance.BaseUnenhancer@73c3e10e, objectFactory:
DefaultConstructorObjectFactory<PositionDto>}
-------------------------------------------------------------------------------
Unenhance strategy: ma.glasnost.orika.unenhance.BaseUnenhancer@73c3e10e
-----end dump of current state-------------------------------] with root cause
java.lang.ClassCastException: com.dlizarra.startuphub.position.Position cannot be cast to com.dlizarra.startuphub.position.Position
at ma.glasnost.orika.generated.Orika_PositionDto_Position_Mapper43322711137530$0.mapAtoB(Orika_PositionDto_Position_Mapper43322711137530$0.java) ~[orika-core-1.4.6.jar:na]
at ma.glasnost.orika.impl.mapping.strategy.UseCustomMapperStrategy.map(UseCustomMapperStrategy.java:67) ~[orika-core-1.4.6.jar:na]
at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:742) ~[orika-core-1.4.6.jar:na]

I really have no idea what's going on here. I don't get where it is trying to cast Position to Position. This happens with every entity/dto class, not just with Position.

I can map any of those classes with no problem when I am unit testing any method, it works perfectly and all the fields get mapped correctly, so I don't think it's an Orika configuration issue. The exception only occurs when I have the webapp deployed in embedded Tomcat and the mapping method gets called within the rest controller method.

It's a simple Spring Boot application and this is the first rest endpoint I wrote in it. Maybe I am missing something in the configuration (I have @EnableAutoConfiguration so there's not that much to configure), but I can't guess what is making Orika throw this exception.

Any ideas or hints about what might be happening here would be very much appreciated.

Thanks!

David Lizárraga
  • 1,172
  • 10
  • 17
  • 4
    Are you using Spring Boot's Dev Tools? It sounds like you're hitting [this known issue](https://github.com/spring-projects/spring-boot/issues/3697). – Andy Wilkinson Oct 25 '15 at 07:14
  • Wow this was a tough one, thanks Andy. Yes I'm using Dev Tools, Orika 1.4.6 and Boot 1.3.0 M5. Now I understand the ClassCastException from Position to Position was due to each class being loaded by a different classloader. I can see you guys are working on a solution since it does not only occur with Orika and the Orika guys are also addressing the issue and it might be fixed for 1.5. – David Lizárraga Oct 25 '15 at 13:14
  • @AndyWilkinson and David I could give you both a big man kiss right now. I'm not sure how many hours I spent on this... but i shudder to think. mapping worked perfectly in unit tests no dice in dev/live run. I was chasing all sorts of other red herrings till i stumbled on this post. cheers – wired00 Jul 30 '16 at 23:23
  • Glad you solved it wired00! The fact that it worked fine in tests was indeed very confusing. Thankfully the Spring Boot team is awesome and they added support to tweak Dev tools through a properties file :) – David Lizárraga Jul 31 '16 at 01:11

1 Answers1

7

I just realized there's a workaround for this error since a few months ago already with Spring Boot 1.4.0 (I believe it's this version), when they introduced the possibility to customize Dev Tools through a properties file.

To solve this issue we just have to:

  1. Create a META-INF folder in src/main/resources.
  2. Create spring-devtools.properties file in it.
  3. Add restart.include.orika=/orika-core.*\.jar to the file.

As stated in the Docs, the restart.include will pull up into the 'restart' classloader any jar matching the Regex. So we are including the orika-core-1.4.6.jar file for example.

David Lizárraga
  • 1,172
  • 10
  • 17
  • This worked perfectly for me, as per that github issue hopefully the orika guys can fix this. Follow those steps precisely restart your application > happy days. – wired00 Jul 30 '16 at 23:27
  • This just saved me a whole bunch of headaches :) I've been looking for some hours now. Thanks! – Georgi Mar 24 '18 at 18:00