Search code examples
faktor-ips

faktor ips ClassCastException when casting IProductComponent to concrete product


I'm using Linkki to create a UI for a faktor ips project. I have a View class in which I added the following page, to display it:

//imports...
public class TestPage extends AbstractPage{

    private static final long serialVersionUID = 1L;
    
    private BindingManager bindingmanager;
    private List<KfzProdukt> produktList;
    
    
    public TestPage() {
        this.bindingmanager = new DefaultBindingManager(ValidationService.NOP_VALIDATION_SERVICE);
        this.produktList= setProduktList();
    }

    @Override
    public void createContent() {
        addSection(new showDetailsPmo(produktList));
    }

    @Override
    protected BindingManager getBindingManager() {
        return bindingmanager;
    }

    public List<KfzProdukt> setProduktList() {
        ClassloaderRuntimeRepository runtimeRepository = ClassloaderRuntimeRepository.create(
                "org/.../faktorips-repository-toc.xml");
        runtimeRepository.setFormulaEvaluatorFactory(new GroovyFormulaEvaluatorFactory());
        return runtimeRepository.getAllProductComponents(KfzProdukt.class);
    }
}

The faktorips-repository-toc.xml looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<FaktorIps-TableOfContents productDataVersion="0.0.1" xmlversion="3.0">
 <ProductComponent implementationClass="org.faktorips.beispiel.model.kfz.KfzProdukt" ipsObjectId="Kfz.NormalVertrag 2022-01" ipsObjectQualifiedName="produkte.NormalVertrag 2022-01" kindId="Kfz.NormalVertrag" versionId="2022-01" xmlResource="org/faktorips/beispiel/model/produkte/NormalVertrag 2022-01.xml"/>
 <ProductComponent implementationClass="org.faktorips.beispiel.model.kfz.KfzHaftpflichtdeckungsTyp" ipsObjectId="Kfz.Haftpflicht 2022-01" ipsObjectQualifiedName="deckungen.Haftpflicht 2022-01" kindId="Kfz.Haftpflicht" versionId="2022-01" xmlResource="org/faktorips/beispiel/model/deckungen/Haftpflicht 2022-01.xml"/>
 <ProductComponent implementationClass="org.faktorips.beispiel.model.kfz.KfzAssistancedeckungsTyp" ipsObjectId="Kfz.Teilkasko 2022-01" ipsObjectQualifiedName="deckungen.Teilkasko 2022-01" kindId="Kfz.Teilkasko" versionId="2022-01" xmlResource="org/faktorips/beispiel/model/deckungen/Teilkasko 2022-01.xml"/>
 <ProductComponent implementationClass="org.faktorips.beispiel.model.kfz.KfzAssistancedeckungsTyp" ipsObjectId="Kfz.Vollkasko 2022-01" ipsObjectQualifiedName="deckungen.Vollkasko 2022-01" kindId="Kfz.Vollkasko" versionId="2022-01" xmlResource="org/faktorips/beispiel/model/deckungen/Vollkasko 2022-01.xml"/>
 <!-- a few  <PolicyCmptType...> entries -->
 <ProductCmptType implementationClass="org.faktorips.beispiel.model.kfz.KfzAssistancedeckungsTyp" ipsObjectId="kfz.KfzAssistancedeckungsTyp" ipsObjectQualifiedName="kfz.KfzAssistancedeckungsTyp"/>
 <ProductCmptType implementationClass="org.faktorips.beispiel.model.kfz.KfzHaftpflichtdeckungsTyp" ipsObjectId="kfz.KfzHaftpflichtdeckungsTyp" ipsObjectQualifiedName="kfz.KfzHaftpflichtdeckungsTyp"/>
 <ProductCmptType implementationClass="org.faktorips.beispiel.model.kfz.KfzProdukt" ipsObjectId="kfz.KfzProdukt" ipsObjectQualifiedName="kfz.KfzProdukt"/>
</FaktorIps-TableOfContents>

When I run the application it doesn't crash, but the List<KfzProdukt> produktList is empty. runtimeRepository.getAllProductComponents(KfzProdukt.class) in the method setProduktList() returns an empty List, which I don't understand, because according to my understanding, it should return a list with one element (the first entry in faktorips-repository-toc.xml).

I tried to get said Product by specifically retrieving it. I added two lines to the method setProduktList(), like this:

public List<KfzProdukt> setProduktList() {
        ClassloaderRuntimeRepository runtimeRepository = ClassloaderRuntimeRepository.create(
                "org/faktorips/beispiel/model/internal/faktorips-repository-toc.xml");
        runtimeRepository.setFormulaEvaluatorFactory(new GroovyFormulaEvaluatorFactory());
        
        IProductComponent productComponent = runtimeRepository.getProductComponent("Kfz.NormalVertrag 2022-01");
        KfzProdukt x = (KfzProdukt) productComponent;
        
        return runtimeRepository.getAllProductComponents(KfzProdukt.class);
    }

Then I get the following error at the line KfzProdukt x = (KfzProdukt) productComponent;:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.likki.fips.kfzlinkki.view.DetailsView': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.likki.fips.kfzlinkki.view.DetailsView]: Constructor threw exception; nested exception is java.lang.ClassCastException: class org.faktorips.beispiel.model.kfz.KfzProdukt cannot be cast to class org.faktorips.beispiel.model.kfz.KfzProdukt (org.faktorips.beispiel.model.kfz.KfzProdukt is in unnamed module of loader 'app'; org.faktorips.beispiel.model.kfz.KfzProdukt is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @6dbef9d8)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:315) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:296) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1372) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:332) ~[spring-beans-5.3.18.jar:5.3.18]
    at com.vaadin.flow.spring.SpringInstantiator.getOrCreate(SpringInstantiator.java:117) ~[vaadin-spring-23.0.3.jar:na]
    at com.vaadin.flow.di.Instantiator.createRouteTarget(Instantiator.java:193) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.lambda$getRouteTarget$1(AbstractNavigationStateRenderer.java:135) ~[flow-server-23.0.3.jar:23.0.3]
    at java.base/java.util.Optional.orElseGet(Optional.java:369) ~[na:na]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.getRouteTarget(AbstractNavigationStateRenderer.java:134) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEventAndPopulateChain(AbstractNavigationStateRenderer.java:488) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.createChainIfEmptyAndExecuteBeforeEnterNavigation(AbstractNavigationStateRenderer.java:469) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handle(AbstractNavigationStateRenderer.java:207) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.component.internal.JavaScriptNavigationStateRenderer.handle(JavaScriptNavigationStateRenderer.java:79) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.component.internal.JavaScriptBootstrapUI.handleNavigation(JavaScriptBootstrapUI.java:317) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.component.internal.JavaScriptBootstrapUI.renderViewForRoute(JavaScriptBootstrapUI.java:280) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.component.internal.JavaScriptBootstrapUI.connectClient(JavaScriptBootstrapUI.java:147) ~[flow-server-23.0.3.jar:23.0.3]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at com.vaadin.flow.server.communication.rpc.PublishedServerEventHandlerRpcHandler.invokeMethod(PublishedServerEventHandlerRpcHandler.java:222) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.rpc.PublishedServerEventHandlerRpcHandler.invokeMethod(PublishedServerEventHandlerRpcHandler.java:199) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.rpc.PublishedServerEventHandlerRpcHandler.invokeMethod(PublishedServerEventHandlerRpcHandler.java:149) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.rpc.PublishedServerEventHandlerRpcHandler.handleNode(PublishedServerEventHandlerRpcHandler.java:132) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.rpc.AbstractRpcInvocationHandler.handle(AbstractRpcInvocationHandler.java:71) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocationData(ServerRpcHandler.java:438) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.ServerRpcHandler.lambda$handleInvocations$1(ServerRpcHandler.java:419) ~[flow-server-23.0.3.jar:23.0.3]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) ~[na:na]
    at com.vaadin.flow.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:419) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:320) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:115) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1567) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:299) ~[flow-server-23.0.3.jar:23.0.3]
    at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:109) ~[vaadin-spring-23.0.3.jar:na]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.60.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:711) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:353) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:313) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.web.servlet.mvc.ServletForwardingController.handleRequestInternal(ServletForwardingController.java:141) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:177) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:51) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) ~[tomcat-embed-core-9.0.60.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.18.jar:5.3.18]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.60.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.18.jar:5.3.18]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.18.jar:5.3.18]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.60.jar:9.0.60]
    at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.likki.fips.kfzlinkki.view.DetailsView]: Constructor threw exception; nested exception is java.lang.ClassCastException: class org.faktorips.beispiel.model.kfz.KfzProdukt cannot be cast to class org.faktorips.beispiel.model.kfz.KfzProdukt (org.faktorips.beispiel.model.kfz.KfzProdukt is in unnamed module of loader 'app'; org.faktorips.beispiel.model.kfz.KfzProdukt is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @6dbef9d8)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:224) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:117) ~[spring-beans-5.3.18.jar:5.3.18]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:311) ~[spring-beans-5.3.18.jar:5.3.18]
    ... 87 common frames omitted
Caused by: java.lang.ClassCastException: class org.faktorips.beispiel.model.kfz.KfzProdukt cannot be cast to class org.faktorips.beispiel.model.kfz.KfzProdukt (org.faktorips.beispiel.model.kfz.KfzProdukt is in unnamed module of loader 'app'; org.faktorips.beispiel.model.kfz.KfzProdukt is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @6dbef9d8)
    at org.likki.fips.kfzlinkki.page.TestPage.setProdukte(TestPage.java:50) ~[classes/:na]
    at org.likki.fips.kfzlinkki.page.TestPage.<init>(TestPage.java:28) ~[classes/:na]
    at org.likki.fips.kfzlinkki.view.DetailsView.<init>(DetailsView.java:32) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[na:na]
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211) ~[spring-beans-5.3.18.jar:5.3.18]
    ... 89 common frames omitted

I don't understand this because I'm not trying to cast a KfzProdukt to a KfzProdukt (because why would I do that, if they are already the same thing). I want to cast a IProductComponent to KfzProdukt

Can someone please explain this to me?


Solution

  • I'm not 100% sure what happens here, but the ClassCastException seems to point to the org.springframework.boot.devtools.restart.classloader.RestartClassLoader from the Spring Devtools. Apparently, it reloads the class org.faktorips.beispiel.model.kfz.KfzProdukt which has already been loaded by the 'app' class loader. You may try to disable reloading as described in this StackOverflow question: Class loader error - unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader.

    This would also explain why runtimeRepository.getAllProductComponents(KfzProdukt.class) returns an empty list, because the KfzProdukt class it uses is not the same (loaded by different classloader) than the one in the repository.