Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Orika ClassCastException in Spring Boot webapp

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!

like image 207
David Lizárraga Avatar asked Oct 25 '15 00:10

David Lizárraga


1 Answers

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.

like image 189
David Lizárraga Avatar answered Oct 24 '22 10:10

David Lizárraga