Quarkus 2.16.x/Camel 3.20.1迁移时Validator组件抛出IllegalArgumentException
问题描述
将Quarkus从2.15.0.Final迁移至2.16.x.Final,同时升级Apache Camel从3.19.0到3.20.1后,Validator组件在集成测试和运行时抛出异常,单元测试可正常执行。
旧版本下正常运行的路由代码片段:
from("someUrl") .to("validator:schemas/<some xsd>") .to("direct:anotherRoute")
新版本抛出的异常:
Caused by: java.lang.IllegalArgumentException: http://www.w3.org/2001/XMLSchema
附加信息
XSD文件
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns="header/v1" xmlns:head="header/v1" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="header/v1" elementFormDefault="qualified" version="1"> <xs:element name="Header" type="Header"/> <xs:complexType name="Header"> <xs:sequence> <xs:element name="CorrelationId" type="xs:string" minOccurs="1" maxOccurs="1"/> <xs:element name="Caller" type="xs:string" minOccurs="1" maxOccurs="1"/> </xs:sequence> </xs:complexType> </xs:schema>
完整路由代码
import org.apache.camel.PropertyInject; import org.apache.camel.builder.RouteBuilder; import javax.enterprise.context.ApplicationScoped; /** * The route. */ @ApplicationScoped public class ValidatorTestEntry extends RouteBuilder { @PropertyInject("x.sftp.host") private String host; @PropertyInject("x.sftp.port") private int port; @PropertyInject("x.sftp.path") private String path; @PropertyInject("x.sftp.username") private String username; @PropertyInject("x.sftp.password") private String password; /** * Ingång i flödet. * */ public void configure() { from(getSftp()) .routeId("validatorTestEntry") .to("log:info?showAll=true") .to("validator:schemas/header_v1.xsd") .to("direct:validatorTestExit"); } private String getSftp() { return "sftp://" + host + ":" + port + "/" + path + "?username=" + username + "&password=" + password + "&delete=true" + "&binary=true" + "&knownHostsFile=known_hosts" + "&throwExceptionOnConnectFailed=true" + "&autoCreate=false" + "&readLock=changed" + "&bridgeErrorHandler=true"; } }
正常运行的单元测试代码
import org.apache.camel.Exchange; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.AdviceWith; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.support.DefaultExchange; import org.apache.camel.test.junit5.CamelTestSupport; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * The unit test. */ public class ValidatorTestEntryTest extends CamelTestSupport { @Override public boolean isUseAdviceWith() { return true; } @Override protected RoutesBuilder createRouteBuilder() { return new ValidatorTestEntry(); } @Test public void sunshine() throws Exception { // arrange AdviceWith.adviceWith(context, "validatorTestEntry", route -> { route.replaceFromWith("direct:start"); route.weaveByToUri("direct:validatorTestExit").replace().to("mock:exit"); }); MockEndpoint exitMock = getMockEndpoint("mock:exit"); exitMock.expectedMessageCount(1); Exchange exchange = new DefaultExchange(context); String body = "<head:Header xmlns:head=\"header/v1\">" + "<head:CorrelationId>String</head:CorrelationId>" + "<head:Caller>String</head:Caller>" + "</head:Header>"; exchange.getIn().setBody(body); // act context.start(); template.send("direct:start", exchange); // assert exitMock.assertIsSatisfied(); assertEquals(body, exchange.getIn().getBody(String.class)); } }
失败的集成测试代码
import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusIntegrationTest; import org.apache.camel.ConsumerTemplate; import org.apache.camel.Exchange; import org.apache.camel.ProducerTemplate; import org.apache.camel.impl.DefaultCamelContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Integration test with Testcontainers. */ @QuarkusTestResource(SftpResources.class) @QuarkusIntegrationTest public class ValidatorTestIT { private ProducerTemplate producerTemplate; private ConsumerTemplate consumerTemplate; @BeforeEach public void setup() { DefaultCamelContext context = new DefaultCamelContext(); context.start(); producerTemplate = context.createProducerTemplate(); consumerTemplate = context.createConsumerTemplate(); } /** * End to end. */ @Test public void shouldPassEndToEnd() { // arrange String fileName = "test.xml"; String expectedBody = "<head:Header xmlns:head=\"header/v1\">" + "<head:CorrelationId>String</head:CorrelationId>" + "<head:Caller>String</head:Caller>" + "</head:Header>"; String endpointUri = "sftp://localhost:{{x.sftp.port}}/upload" + "?username={{x.sftp.username}}&password={{x.sftp.password}}&fileName=" + fileName; producerTemplate.sendBody(endpointUri, expectedBody); // act Exchange actualExchange = consumerTemplate.receive("direct:validatorTestExit", 10000); // assert assertEquals(expectedBody, actualExchange.getIn().getBody(String.class)); assertEquals(fileName, actualExchange.getIn().getHeader(Exchange.FILE_NAME, fileName)); } }
解决方案
该问题源于Quarkus 2.16.x升级后默认启用了Bean Validation严格模式,导致Validator组件加载XSD时无法正常处理w3c标准Schema引用。以下是几种修复方案:
方法1:快速禁用严格模式
在application.properties中添加配置,关闭Bean Validation的严格模式:
quarkus.bean-validation.strict=false
方法2:配置Validator组件使用Schema缓存
修改路由中的Validator端点,添加schemaCache=true参数,强制缓存已加载的Schema:
.to("validator:schemas/header_v1.xsd?schemaCache=true")
方法3:修正集成测试上下文问题
集成测试中手动创建DefaultCamelContext会脱离Quarkus的托管上下文,导致配置不生效。应注入Quarkus管理的Camel上下文:
import jakarta.inject.Inject; import org.apache.camel.CamelContext; // ... @Inject CamelContext camelContext; @BeforeEach public void setup() { producerTemplate = camelContext.createProducerTemplate(); consumerTemplate = camelContext.createConsumerTemplate(); }
其中方法1是最简便的快速修复方式,方法3能确保集成测试与运行时环境一致,避免配置差异引发的问题。
内容的提问来源于stack exchange,提问作者Magnus
相关产品推荐
相关产品推荐

