Search code examples
springspring-bootspring-integrationspring-el

org.springframework.expression.spel.SpelEvaluationException: EL1005E: Type cannot be found 'com.company.package.SomeClass'


Heads up: I'm using Spring Boot 2.2.0.RELEASE, so it's not related to this.

I have a MessagingGateway as follows:

@Gateway(
            requestChannel = "myChannel",
            headers = @GatewayHeader(
                    name = "tenantId",
                    expression = "T(com.company.package.ContextHolder).getContext()?.tenant?.identifier"))
    void publishEvent(@Header(value = "eventType") String eventType,
                      @Payload Object payload);

My ContextHolder class is similar to SecurityContextHolder or RequestContextHolder, and I put some basic request related info in it, just like SecurityContext.

The issue is, this works perfectly on my machine, and for the most calls to the publishEvent method it does on the server, as well. However, for some methods, I get:

org.springframework.expression.spel.SpelEvaluationException: EL1005E: Type cannot be found 'com.company.package.ContextHolder'
        at org.springframework.expression.spel.support.StandardTypeLocator.findType(StandardTypeLocator.java:117) ~[spring-expression-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.expression.spel.ExpressionState.findType(ExpressionState.java:155) ~[spring-expression-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.expression.spel.ast.TypeReference.getValueInternal(TypeReference.java:69) ~[spring-expression-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:55) ~[spring-expression-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:91) ~[spring-expression-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112) ~[spring-expression-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:330) ~[spring-expression-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.evaluateHeaders(GatewayMethodInboundMessageMapper.java:213) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.access$1000(GatewayMethodInboundMessageMapper.java:86) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper$DefaultMethodArgsMessageMapper.buildMessage(GatewayMethodInboundMessageMapper.java:434) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper$DefaultMethodArgsMessageMapper.toMessage(GatewayMethodInboundMessageMapper.java:340) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper$DefaultMethodArgsMessageMapper.toMessage(GatewayMethodInboundMessageMapper.java:288) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.mapArgumentsToMessage(GatewayMethodInboundMessageMapper.java:198) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.toMessage(GatewayMethodInboundMessageMapper.java:192) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.toMessage(GatewayMethodInboundMessageMapper.java:86) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:111) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.doConvert(AbstractMessageSendingTemplate.java:182) ~[spring-messaging-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:150) ~[spring-messaging-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:143) ~[spring-messaging-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.MessagingGatewaySupport.send(MessagingGatewaySupport.java:417) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayProxyFactoryBean.sendOrSendAndReceive(GatewayProxyFactoryBean.java:576) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:508) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayProxyFactoryBean.doInvoke(GatewayProxyFactoryBean.java:478) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:468) ~[spring-integration-core-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.0.RELEASE.jar!/:5.2.0.RELEASE]
        at com.sun.proxy.$Proxy168.publishEvent(Unknown Source) ~[na:na]
        at com.company.project.services.SomeService.lambda$bulk$2(SomeService.java:434) ~[project-api-0.5.0.RELEASE.jar!/:na]
        at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) ~[na:na]
        at java.base/java.util.stream.SpinedBuffer.forEach(SpinedBuffer.java:246) ~[na:na]
        at java.base/java.util.stream.Nodes$SpinedNodeBuilder.forEach(Nodes.java:1270) ~[na:na]
        at java.base/java.util.stream.Nodes$InternalNodeSpliterator$OfRef.forEachRemaining(Nodes.java:1105) ~[na:na]
        at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
        at java.base/java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290) ~[na:na]
        at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746) ~[na:na]
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) ~[na:na]
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) ~[na:na]
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) ~[na:na]
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) ~[na:na]
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) ~[na:na]

and the bulk method for SomeService is somewhat like:

StreamSupport
    .stream(worksheet.spliterator(), true)
    .skip(1)
    .forEach(row -> {

        // insert row into db and publish message

        this.applicationEventGateway.publishEvent("rowCreated", row);

    });

Worksheet being an Apache POI worksheet. I understand that the method is called in a parallel stream, but would it result in an error with class not being found? What am I missing?


Solution

  • Feels like that parallel for the Stream makes it looking into a different ClassLoader.

    Would be great if you can confirm how it works without parallel.

    It also feel like Spring Integration's IntegrationEvaluationContextFactoryBean can be fixed for a StandardTypeLocator with a ClassLoader from BeanFactory.

    You may look into a workaround like creating a bean for IntegrationEvaluationContextFactoryBean with the IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME name and set typeLocator to the StandardTypeLocator based on the BeanFactory class loader:

    @Bean(name = IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)
    public IntegrationEvaluationContextFactoryBean integrationEvaluationContext(ConfigurableBeanFactory beanFactory) {
        final IntegrationEvaluationContextFactoryBean context = new IntegrationEvaluationContextFactoryBean();
        context.setTypeLocator(new StandardTypeLocator(beanFactory.getBeanClassLoader()));
        context.afterPropertiesSet();
        return context;
    }
    

    If that is a case, please, raise a GH issue, so we will fix it shortly.