第 78 章 CXF


CXF Component

cxf: 组件提供与 Apache CXF 集成,以连接到 CXF 中托管的 JAX-WS 服务。

Maven 用户必须将此组件的 pom.xml 添加以下依赖项:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-cxf</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>
Copy to Clipboard Toggle word wrap
注意

如果要了解 CXF 依赖项,请参阅 WHICH-JARS 文本文件。

注意

当在流模式中使用 CXF 时(请参阅 DataFormat 选项),然后阅读有关 流缓存 的信息。

Camel on EAP 部署

此组件由 EAP (Wildfly Camel)框架上的 Camel 支持,该框架在 Red Hat JBoss Enterprise Application Platform (JBoss EAP)容器上提供了简化的部署模型。

CXF 组件与同样使用 Apache CXF 的 JBoss EAP webservices susbsystem 集成。如需更多信息,请参阅 JAX-WS

注意

目前,EAP 子系统的 Camel 不支持 CXF 或 Restlet 用户。但是,可以使用 CamelProxy 模拟 CXF 使用者的行为。

URI 格式

cxf:bean:cxfEndpoint[?options]
Copy to Clipboard Toggle word wrap

其中 cxfEndpoint 代表一个 bean ID,它引用 Spring bean registry 中的 bean。使用此 URI 格式时,大多数端点详情都在 bean 定义中指定。

cxf://someAddress[?options]
Copy to Clipboard Toggle word wrap

其中 someAddress 指定 CXF 端点的地址。使用此 URI 格式时,大多数端点详情都通过选项来指定。

对于以上任一样式,您可以按如下方式在 URI 中附加选项:

cxf:bean:cxfEndpoint?wsdlURL=wsdl/hello_world.wsdl&dataFormat=PAYLOAD
Copy to Clipboard Toggle word wrap

选项

Expand

名称

必填

描述

wsdlURL

WSDL 的位置。默认情况下,WSDL 从端点地址获取。例如:

file://local/wsdl/hello.wsdl or wsdl/hello.wsdl

serviceClass

SEI (Service Endpoint Interface)类的名称。此类可以具有,但不需要 JSR181 注释。从 2.0 开始, 只有 POJO 模式需要这个选项。如果提供了 wsdlURL 选项,则 PAYLOAD 和 MESSAGE 模式不需要 serviceClass。当在没有 serviceClass 的情况下使用 wsdlURL 选项时,必须提供 serviceName 和 portName (endpointName for Spring configuration)选项。

从 2.0 开始,可以使用 \# 表示法来引用 registry 中的 serviceClass 对象实例。

请注意,引用的对象不能是一个 Proxy (Spring AOP Proxy 为 OK), 因为它依赖于非 Spring AOP Proxy 的 Object.getClass ().getName () 方法。

2.8 开始, 可以为 PAYLOAD 和 MESSAGE 模式省略 wsdlURL 和 serviceClass 选项。当它们被省略时,任意 XML 元素可以放在 PAYLOAD 模式的 CxfPayload 的正文中,以方便 CXF Dispatch 模式。

例如: org.apache.camel.Hello

serviceName

只有 WSDL 中存在多个 serviceName

此服务正在实现的服务名称,它映射到 wsdl:service@name。例如:

{http://org.apache.camel}ServiceName

endpointName

只有在 serviceName 下有多个 portName 时才存在,并且 camel-cxf 消费者需要自 camel 2.2 开始需要它

此服务实施的端口名称,它映射到 wsdl:port@name。例如:

{http://org.apache.camel}PortName

dataFormat

CXF 端点支持哪些消息数据格式。可能的值有: POJO (默认)、PAY LOADMESSAGE

relayHeaders

定义 CXF 端点中继标头和路由。请参阅 “relayHeaders 选项的描述”一节。目前仅在 dataFormat=POJODefault:true示例:true,false时才可用

嵌套

CXF 端点制作者调用的操作类型。可能的值有: true,false (默认)

wrappedStyle

2.5.0 开始,WSDL 样式用于描述在 SOAP 正文中如何表示参数。如果值为 false,则 CXF 选择文档字面解样式,如果值为 true,则 CXF 选择文档封装样式

setDefaultBus

deprecated : 指定是否为这个端点使用默认的 CXF 总线。可能的值有: true,false (默认)这个选项已弃用。使用 Camel 2.16 中的 defaultBus

defaultBus

deprecated : 指定是否为这个端点使用默认的 CXF 总线。可能的值有: true,false (默认)这个选项已弃用。使用 Camel 2.16 中的 defaultBus

bus

使用 \# 表示法引用 registry 的 bus 对象(如 bus=\#busName )。所引用的对象必须是 org.apache.cxf.Bus 的实例。

默认情况下,使用 CXF 总线 Factory 创建的默认总线。

cxfBinding

使用 \# 表示法引用 registry 的 CXF 绑定对象,例如: cxfBinding=\#bindingName。引用的对象必须是 org.apache.camel.component.cxf.CxfBinding 实例。

headerFilterStrategy

使用 \# 表示法引用 registry 的 header 过滤器策略对象,例如 headerFilterStrategy=\#strategyName。引用的对象必须是 org.apache.camel.spi.HeaderFilterStrategy 的实例。

loggingFeatureEnabled

在 2.3 中,这个选项允许 CXF Logging 功能将入站和出站 SOAP 消息写入日志。可能的值有: true,false (默认)

defaultOperationName

在 2.4 中,这个选项设置调用远程服务的 CxfProducer 使用的默认 operationName。例如:

defaultOperationName=greetMe

defaultOperationNamespace

在 2.4 中,这个选项设置调用远程服务的 CxfProducer 使用的默认 operationNamespace。例如:

defaultOperationNamespace=http://apache.org/hello_world_soap_http

同步

在 2.5 中,这个选项可让 CXF 端点决定使用 sync 或 async API 进行底层工作。默认值为 false,这意味着 camel-cxf 端点默认尝试使用 async API。

publishedEndpointUrl

在 2.5 中,此选项将覆盖使用服务地址 URL 和 ?wsdl 访问的已发布 WSDL 中出现的端点 URL。例如:

publshedEndpointUrl=http://example.com/service

properties.propName

Camel 2.8: 允许您在端点 URI 中设置自定义 CXF 属性。例如,设置 properties.mtom-enabled=true 来启用 MTOM。为确保 CXF 在开始调用时不会切换线程,您可以设置 properties.org.apache.cxf.interceptor.OneWayProcessorInterceptor.USE_ORIGINAL_THREAD=true

allowStreaming

2.8.2 中的新内容.这个选项控制在 PAYLOAD 模式运行时 CXF 组件(请参见下面的),DOM 将传入的消息解析为 DOM Elements,或将有效负载保留为 javax.xml.transform.Source 对象,在某些情况下允许流。

skipFaultLogging

2.11 中的新内容。这个选项控制 PhaseInterceptorChain 是否跳过记录它捕获的 Fault。

cxfEndpointConfigurer

Camel 2.11 的新内容。这个选项可以应用 org.apache.camel.component.cxf.CxfEndpointConfigurer 的实现,它支持 以编程方式配置 CXF 端点。从 Camel 2.15.0 开始, 用户可以通过实施 CxfEndpointConfigurer 的 configure{Server/Client} 方法来配置 CXF 服务器和客户端。

username

Camel 2.12.3 这个选项的新用于为 CXF 客户端设置用户名的基本身份验证信息。

password

Camel 2.12.3 这个选项的新用于为 CXF 客户端设置密码的基本身份验证信息。

continuationTimeout

Camel 2.14.0 的新选项用于设置 CXF continuation 超时,当 CXF 服务器使用 Jetty 或 Servlet 传输时,默认可在 CxfConsumer 中使用。(在 Camel 2.14.0 之前,CxfConsumer 仅将延续超时设置为 0,这意味着持续暂停操作永远不会超时。)

默认: 30000 示例 :continuation=80000

serviceNameportNameQNames,因此如果您提供它们,请确保它们带有其 {namespace} 前缀,如上例中所示。

dataformats 的描述

Expand

DataFormat

描述

POJO

POJO (解释旧 Java 对象)是目标服务器上调用的方法的 Java 参数。支持 Protocol 和 Logical JAX-WS 处理程序。

PAYLOAD

PAYLOAD 是应用 CXF 端点的消息配置后的消息有效负载( soap:body的内容)。仅支持 Protocol JAX-WS 处理程序。不支持逻辑 JAX-WS 处理程序。

MESSAGE

MESSAGE 是从传输层接收的原始消息。如果您使用此类 DataFormat,则不会假设涉及或更改流,则一些 CXF 拦截器会被删除,因此您无法在 camel-cxf consumer 和 JAX-WS 处理程序后看到任何 soap 标头。

CXF_MESSAGE

Camel 2.8.2 的新内容,CXF_MESSAGE 通过将消息从传输层转换为原始 SOAP 消息来调用 CXF 拦截器的完整功能

您可以通过检索交换属性 CamelCXFDataFormat 来确定交换的数据格式模式。Exchange key 常量在 org.apache.camel.component.cxf.CxfConstants.DATA_FORMAT_PROPERTY 中定义。

使用 Apache Aries 蓝图配置 CXF 端点.

从 Camel 2.8 开始,支持为您的 CXF 端点使用 Aries 蓝图依赖项注入。模式与 Spring 模式非常相似,因此转换非常透明。

例如:

 <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
            xmlns:camel-cxf="http://camel.apache.org/schema/blueprint/cxf"
 	   xmlns:cxfcore="http://cxf.apache.org/blueprint/core"
            xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">

       <camel-cxf:cxfEndpoint id="routerEndpoint"
                      address="http://localhost:9001/router"
                      serviceClass="org.apache.servicemix.examples.cxf.HelloWorld">
         <camel-cxf:properties>
             <entry key="dataFormat" value="MESSAGE"/>
         </camel-cxf:properties>
      </camel-cxf:cxfEndpoint>

      <camel-cxf:cxfEndpoint id="serviceEndpoint"
			address="http://localhost:9000/SoapContext/SoapPort"
                     serviceClass="org.apache.servicemix.examples.cxf.HelloWorld">
    </camel-cxf:cxfEndpoint>

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <route>
            <from uri="routerEndpoint"/>
            <to uri="log:request"/>
        </route>
    </camelContext>

</blueprint>
Copy to Clipboard Toggle word wrap

目前,endpoint 元素是第一个支持的 CXF namespacehandler。

您还可以使用类似于 spring 中的 bean 引用

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
           xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws"
           xmlns:cxf="http://cxf.apache.org/blueprint/core"
           xmlns:camel="http://camel.apache.org/schema/blueprint"
           xmlns:camelcxf="http://camel.apache.org/schema/blueprint/cxf"
           xsi:schemaLocation="
             http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
             http://cxf.apache.org/blueprint/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.xsd
             http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd
             ">

    <camelcxf:cxfEndpoint id="reportIncident"
                     address="/camel-example-cxf-blueprint/webservices/incident"
                     wsdlURL="META-INF/wsdl/report_incident.wsdl"
                     serviceClass="org.apache.camel.example.reportincident.ReportIncidentEndpoint">
    </camelcxf:cxfEndpoint>

    <bean id="reportIncidentRoutes" class="org.apache.camel.example.reportincident.ReportIncidentRoutes" />

    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
        <routeBuilder ref="reportIncidentRoutes"/>
    </camelContext>

</blueprint>
Copy to Clipboard Toggle word wrap

如何在 MESSAGE 模式中启用 CXF LoggingOutInterceptor

CXF 的 LoggingOutInterceptor 输出进入 wire 到日志记录系统的出站消息(java.util.logging)。由于 LoggingOutInterceptor 位于 PRE_STREAM 阶段(但 PRE_STREAM 阶段以 MESSAGE 模式删除),因此您必须配置 LoggingOutInterceptor 以便在 WRITE 阶段运行。以下是一个示例。

   <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor">
        <!--  it really should have been user-prestream but CXF does have such phase! -->
        <constructor-arg value="target/write"/>
   </bean>

<cxf:cxfEndpoint id="serviceEndpoint" address="http://localhost:9002/helloworld"
	serviceClass="org.apache.camel.component.cxf.HelloService">
	<cxf:outInterceptors>
	    <ref bean="loggingOutInterceptor"/>
	</cxf:outInterceptors>
	<cxf:properties>
		<entry key="dataFormat" value="MESSAGE"/>
	</cxf:properties>
</cxf:cxfEndpoint>
Copy to Clipboard Toggle word wrap

relayHeaders 选项的描述

从 JAXWS WSDL -first 开发者的视角,有带 和带外 的标头。

in-band 标头是标头,作为端点的 WSDL 绑定合同的一部分(如 SOAP 标头)明确定义。

带外 标头是通过线上序列化但不是 WSDL 绑定合同的一部分的标头。

标头转发/过滤是双向的。

当路由具有 CXF 端点且开发人员需要具有 on-the-wire 标头(如 SOAP 标头)时,将路由转发给另一个 JAXWS 端点,然后将 relayHeaders 设置为 true,这是默认值。

仅适用于 POJO 模式

relayHeaders=true 设置表示转发标头的意图。关于给定标头是否被中继到实施 MessageHeadersRelay 接口的可插拔实例的实际决定。使用 MessageHeadersRelay 的具体实施来确定是否需要转发标头。已有 SoapMessageHeadersRelay 的实现,它将自身绑定到众所周知的 SOAP 命名空间。目前,仅过滤带外标头,当 relayHeaders=true 时始终转发带内标头。如果线上有一个标头,其命名空间对运行时未知,则使用回退 DefaultMessageHeadersRelay,这只是允许所有标头进行转发。

relayHeaders=false 设置断言,所有标头( in-band 和 out-of-band)都会被丢弃。

您可以插件自己的 MessageHeadersRelay 实现覆盖,或向中继列表添加额外的消息。要覆盖预加载中继实例,请确保您的 MessageHeadersRelay 实现服务与您要覆盖的命名空间相同。另请注意,覆盖中继必须将所有命名空间作为您要覆盖的命名空间服务,否则会抛出有关路由启动的运行时异常,因为这会在命名空间中引入转发实例映射的不确定性。

<cxf:cxfEndpoint ...>
   <cxf:properties>
     <entry key="org.apache.camel.cxf.message.headers.relays">
       <list>
         <ref bean="customHeadersRelay"/>
       </list>
     </entry>
   </cxf:properties>
 </cxf:cxfEndpoint>
 <bean id="customHeadersRelay" class="org.apache.camel.component.cxf.soap.headers.CustomHeadersRelay"/>
Copy to Clipboard Toggle word wrap

查看显示如何在这里转发/过滤标头的测试:

Link:https://svn.apache.org/repos/asf/camel/branches/camel-1.x/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java[https://svn.apache.org/repos/asf/camel/branches/camel-1.x/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/soap/headers/CxfMessageHeadersRelayTest.java ]

从版本 2.0 开始的更改

  • 支持 POJOPAYLOAD 模式。在 POJO 模式中,只有带外消息标头可用于过滤,因为已由 CXF 处理并从标头列表中删除。in-band 标头会合并到 POJO 模式的 MessageContentList 中。camel-cxf 组件会试图从 MessageContentList 中删除带内标头的任何尝试,如果需要过滤 in-band 标头,请使用 PAYLOAD 模式或插入(pretty simple) CXF 拦截器/JAXWS Handler 到 CXF 端点。
  • Message Header Relay 机制已合并到 CxfHeaderFilterStrategy 中。relayHeaders 选项、其语义和默认值保持不变,但它是 CxfHeaderFilterStrategy 的属性。下面是一个配置它的示例:

    <bean id="dropAllMessageHeadersStrategy" class="org.apache.camel.component.cxf.common.header.CxfHeaderFilterStrategy">
    
        <!--  Set relayHeaders to false to drop all SOAP headers -->
        <property name="relayHeaders" value="false"/>
    
    </bean>
    Copy to Clipboard Toggle word wrap

    然后,您的端点可以引用 CxfHeaderFilterStrategy

    <route>
        <from uri="cxf:bean:routerNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
        <to uri="cxf:bean:serviceNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
    </route>
    Copy to Clipboard Toggle word wrap
  • MessageHeadersRelay 接口稍微改变,并被重命名为 MessageHeaderFilter。它是 CxfHeaderFilterStrategy 的一个属性。以下是配置用户定义的消息标头过滤器的示例:

    <bean id="customMessageFilterStrategy" class="org.apache.camel.component.cxf.common.header.CxfHeaderFilterStrategy">
        <property name="messageHeaderFilters">
            <list>
                <!--  SoapMessageHeaderFilter is the built in filter. It can be removed by omitting it. -->
                <bean class="org.apache.camel.component.cxf.common.header.SoapMessageHeaderFilter"/>
    
                <!--  Add custom filter here -->
                <bean class="org.apache.camel.component.cxf.soap.headers.CustomHeaderFilter"/>
            </list>
        </property>
    </bean>
    Copy to Clipboard Toggle word wrap
  • relayHeaders 外,在 CxfHeaderFilterStrategy 中可以配置新的属性。
Expand

名称

描述

type

必需?

默认值

relayHeaders

所有消息标头都由 Message Header Filters 处理

布尔值

true (1.6.1 行为)

relayAllMessageHeaders

所有消息标头都会被传播(不需要由 Message Header Filters 处理)

布尔值

false (1.6.1 行为)

allowFilterNamespaceClash

处理激活命名空间中的重叠过滤器。如果值为 true,则最后一个优先。如果值为 false,它会抛出异常。

布尔值

false (1.6.1 行为)

使用 Spring 配置 CXF 端点

您可以使用如下所示的 Spring 配置文件配置 CXF 端点,您也可以将端点嵌入到 camelContext 标签中。当您调用服务端点时,您可以将 operationNameoperationNamespace 标头设置为显式状态您要调用的操作。

注意在 Camel 2.x 中,我们更改为使用 http://camel.apache.org/schema/cxf 作为 CXF 端点的目标命名空间。

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:cxf="http://camel.apache.org/schema/cxf"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd     ">
 ...
Copy to Clipboard Toggle word wrap
注意

务必包含 root beans 元素中指定的 JAX-WS schemaLocation 属性。这允许 CXF 验证文件并是必需的。另请注意,< cxf:cxfEndpoint/&gt; 标签末尾的命名空间声明是必需的,因为此标签的属性值目前不支持组合的 {namespace}localName 语法。

cxf:cxfEndpoint 元素支持许多额外的属性:

Expand

Name

portName

此服务实施的端点名称,它映射到 wsdl:port@name。格式为 ns:PORT_NAME,其中 ns 是在这个范围内有效的命名空间前缀。

serviceName

此服务正在实现的服务名称,它映射到 wsdl:service@name。格式为 ns:SERVICE_NAME,其中 ns 是在这个范围内有效的命名空间前缀。

wsdlURL

WSDL 的位置。可以位于类路径、文件系统上,也可以远程托管。

bindingId

要使用的服务模型的 bindingId

address

服务发布地址。

bus

JAX-WS 端点中使用的总线名称。

serviceClass

SEI (Service Endpoint Interface)类的类名称,它们可能具有 JSR181 注释。

它还支持许多子元素:

Expand

Name

cxf:inInterceptors

此端点的传入拦截器。< bean> 或 &lt; ref> 列表。

cxf:inFaultInterceptors

此端点的传入故障拦截器。< bean> 或 &lt; ref> 列表。

cxf:outInterceptors

此端点的传出拦截器。< bean> 或 &lt; ref> 列表。

cxf:outFaultInterceptors

此端点的传出故障拦截器。< bean> 或 &lt; ref> 列表。

cxf:properties

映射到提供给 JAX-WS 端点的属性。

cxf:handlers

用于提供给 JAX-WS 端点的 JAX-WS 处理程序列表。

cxf:dataBinding

您可以指定端点中使用的 DataBinding。这可以通过 Spring < bean class="MyDataBinding"/> 语法提供。

cxf:binding

您可以指定要使用的此端点的 BindingFactory。这可以通过 Spring < bean class="MyBindingFactory"/> 语法提供。

cxf:features

包含此端点的拦截器的功能。< bean&gt; s 或 < ref>s 列表

cxf:schemaLocations

要使用的端点的 schema 位置。< schemaLocation> s列表

cxf:serviceFactory

要使用此端点的服务工厂。这可以通过 Spring < bean class="MyServiceFactory"/> 语法提供

您可以找到更多高级示例,其中演示了如何提供拦截器、属性和处理程序 :http://cwiki.apache.org/CXF20DOC/jax-ws-configuration.html

注意

您可以使用 CXF:properties 从 Spring 配置文件中设置 CXF 端点的 dataFormatsetDefaultBus 属性,如下所示:

<cxf:cxfEndpoint id="testEndpoint" address="http://localhost:9000/router"
     serviceClass="org.apache.camel.component.cxf.HelloService"
     endpointName="s:PortName"
     serviceName="s:ServiceName"
     xmlns:s="http://www.example.com/test">
     <cxf:properties>
       <entry key="dataFormat" value="MESSAGE"/>
       <entry key="setDefaultBus" value="true"/>
     </cxf:properties>
   </cxf:cxfEndpoint>
Copy to Clipboard Toggle word wrap

CXF 的默认日志记录器是 java.util.logging。如果要将其更改为 log4j,请按如下所示进行操作。在 classpath 中创建一个名为 META-INF/cxf/org.apache.cxf.logger 的文件。此文件必须在一行中包含类的完全限定名称 org.apache.cxf.common.logging.Log4jLogger,没有注释。

如何使用 xml start 文档让 camel-cxf 响应消息

如果您使用一些 SOAP 客户端,如 PHP,可能会导致此类错误,因为 CXF 不会添加 XML 启动文档 < ?xml version="1.0" encoding="utf-8"?>

Error:sendSms: SoapFault exception: [Client] looks like we got no XML document in [...]
Copy to Clipboard Toggle word wrap

要解决这个问题,您只需要告诉 StaxOutInterceptor 为您编写 XML 启动文档。

public class WriteXmlDeclarationInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    public WriteXmlDeclarationInterceptor() {
        super(Phase.PRE_STREAM);
        addBefore(StaxOutInterceptor.class.getName());
    }

    public void handleMessage(SoapMessage message) throws Fault {
        message.put("org.apache.cxf.stax.force-start-document", Boolean.TRUE);
    }

}
Copy to Clipboard Toggle word wrap

您可以添加客户拦截器,并将其配置为 camel-cxf endpont

<cxf:cxfEndpoint id="routerEndpoint" address="http://localhost:${CXFTestSupport.port2}/CXFGreeterRouterTest/CamelContext/RouterPort"
 		serviceClass="org.apache.hello_world_soap_http.GreeterImpl"
 		skipFaultLogging="true">
     <cxf:outInterceptors>
         <!-- This interceptor forces the CXF server send the XML start document to client -->
         <bean class="org.apache.camel.component.cxf.WriteXmlDeclarationInterceptor"/>
     </cxf:outInterceptors>
     <cxf:properties>
         <!-- Set the publishedEndpointUrl which could override the service address from generated WSDL as you want -->
         <entry key="publishedEndpointUrl" value="http://www.simple.com/services/test" />
     </cxf:properties>
 </cxf:cxfEndpoint>
Copy to Clipboard Toggle word wrap

或者,如果您使用 Camel 2.4,则为它添加一个消息标头。

 // set up the response context which force start document
 Map<String, Object> map = new HashMap<String, Object>();
 map.put("org.apache.cxf.stax.force-start-document", Boolean.TRUE);
 exchange.getOut().setHeader(Client.RESPONSE_CONTEXT, map);
Copy to Clipboard Toggle word wrap

camel-cxf 端点消费者 POJO 数据格式基于 cxf 调用器,因此消息头具有名为 CxfConstants.OPERATION_NAME 的属性,消息正文是 SEI 方法参数的列表。

public class PersonProcessor implements Processor {

    private static final transient Logger LOG = LoggerFactory.getLogger(PersonProcessor.class);

    @SuppressWarnings("unchecked")
    public void process(Exchange exchange) throws Exception {
        LOG.info("processing exchange in camel");

        BindingOperationInfo boi = (BindingOperationInfo)exchange.getProperty(BindingOperationInfo.class.toString());
        if (boi != null) {
            LOG.info("boi.isUnwrapped" + boi.isUnwrapped());
        }
        // Get the parameters list which element is the holder.
        MessageContentsList msgList = (MessageContentsList)exchange.getIn().getBody();
        Holder<String> personId = (Holder<String>)msgList.get(0);
        Holder<String> ssn = (Holder<String>)msgList.get(1);
        Holder<String> name = (Holder<String>)msgList.get(2);

        if (personId.value == null || personId.value.length() == 0) {
            LOG.info("person id 123, so throwing exception");
            // Try to throw out the soap fault message
            org.apache.camel.wsdl_first.types.UnknownPersonFault personFault =
                new org.apache.camel.wsdl_first.types.UnknownPersonFault();
            personFault.setPersonId("");
            org.apache.camel.wsdl_first.UnknownPersonFault fault =
                new org.apache.camel.wsdl_first.UnknownPersonFault("Get the null value of person name", personFault);
            // Since camel has its own exception handler framework, we can't throw the exception to trigger it
            // We just set the fault message in the exchange for camel-cxf component handling and return
            exchange.getOut().setFault(true);
            exchange.getOut().setBody(fault);
            return;
        }

        name.value = "Bonjour";
        ssn.value = "123";
        LOG.info("setting Bonjour as the response");
        // Set the response message, first element is the return value of the operation,
        // the others are the holders of method parameters
        exchange.getOut().setBody(new Object[] {null, personId, ssn, name});
    }

}
Copy to Clipboard Toggle word wrap

如何以 POJO 数据格式为 camel-cxf 端点准备消息

camel-cxf 端点制作者基于 cxf 客户端 API。首先,您需要在消息标头中指定操作名称,然后将 method 参数添加到列表中,并使用此参数列表初始化消息。响应消息的正文是 messageContentsList,您可以从该列表中获取结果。

如果您没有在消息标头中指定操作名称,CxfProducer 会尝试使用 CxfEndpoint 中的 defaultOperationName。如果没有在 CxfEndpoint 上设置 defaultOperationName,它会从操作列表中获取第一个操作名称。

如果要从消息正文中获取对象数组,您可以使用 message.getbody (Object[].class) 获取正文,如下所示:

Exchange senderExchange = new DefaultExchange(context, ExchangePattern.InOut);
final List<String> params = new ArrayList<String>();
// Prepare the request message for the camel-cxf procedure
params.add(TEST_MESSAGE);
senderExchange.getIn().setBody(params);
senderExchange.getIn().setHeader(CxfConstants.OPERATION_NAME, ECHO_OPERATION);

Exchange exchange = template.send("direct:EndpointA", senderExchange);

org.apache.camel.Message out = exchange.getOut();
// The response message's body is an MessageContentsList which first element is the return value of the operation,
// If there are some holder parameters, the holder parameter is filled in the reset of List.
// The result is extract from the MessageContentsList with the String class type
MessageContentsList result = (MessageContentsList)out.getBody();
LOG.info("Received output text: " + result.get(0));
Map<String, Object> responseContext = CastUtils.cast((Map<?, ?>)out.getHeader(Client.RESPONSE_CONTEXT));
assertNotNull(responseContext);
assertEquals("The response context", "UTF-8", responseContext.get(org.apache.cxf.message.Message.ENCODING));
assertEquals("Reply body on Camel is wrong", "echo " + TEST_MESSAGE, result.get(0));
Copy to Clipboard Toggle word wrap

在 Apache Camel 2.0 中: CxfMessage.getBody () 返回 org.apache.camel.component.cxf.CxfPayload 对象,它具有 SOAP 消息标头和 Body 元素的 getters。这个更改启用了从 Apache Camel 消息分离原生 CXF 消息。

protected RouteBuilder createRouteBuilder() {
    return new RouteBuilder() {
        public void configure() {
            from(SIMPLE_ENDPOINT_URI + "&dataFormat=PAYLOAD").to("log:info").process(new Processor() {
                @SuppressWarnings("unchecked")
                public void process(final Exchange exchange) throws Exception {
                    CxfPayload<SoapHeader> requestPayload = exchange.getIn().getBody(CxfPayload.class);
                    List<Source> inElements = requestPayload.getBodySources();
                    List<Source> outElements = new ArrayList<Source>();
                    // You can use a customer toStringConverter to turn a CxfPayLoad message into String as you want
                    String request = exchange.getIn().getBody(String.class);
                    XmlConverter converter = new XmlConverter();
                    String documentString = ECHO_RESPONSE;

                    Element in = new XmlConverter().toDOMElement(inElements.get(0));
                    // Just check the element namespace
                    if (!in.getNamespaceURI().equals(ELEMENT_NAMESPACE)) {
                        throw new IllegalArgumentException("Wrong element namespace");
                    }
                    if (in.getLocalName().equals("echoBoolean")) {
                        documentString = ECHO_BOOLEAN_RESPONSE;
                        checkRequest("ECHO_BOOLEAN_REQUEST", request);
                    } else {
                        documentString = ECHO_RESPONSE;
                        checkRequest("ECHO_REQUEST", request);
                    }
                    Document outDocument = converter.toDOMDocument(documentString);
                    outElements.add(new DOMSource(outDocument.getDocumentElement()));
                    // set the payload header with null
                    CxfPayload<SoapHeader> responsePayload = new CxfPayload<SoapHeader>(null, outElements, null);
                    exchange.getOut().setBody(responsePayload);
                }
            });
        }
    };
}
Copy to Clipboard Toggle word wrap

如何在 POJO 模式中获取和设置 SOAP 标头

POJO 表示当 CXF 端点生成或消耗 Camel 交换时,数据格式是 Java 对象列表。虽然 Apache Camel 在此模式下将消息正文公开为 POJO,但 CXF 组件仍提供对读取和写入 SOAP 标头的访问。但是,由于 CXF 拦截器在处理后从标头列表中删除带中的 SOAP 标头,因此只有 POJO 模式中的带外 SOAP 标头可用。

以下示例演示了如何获取/设置 SOAP 标头。假设我们有一个路由,它从一个 CXF 端点转发到另一个。也就是说,SOA Client Apache Camel CXF 服务。在请求超出 CXF 服务前,可以附加两个处理器在(1)处获取/插入 SOAP 标头,然后再返回 SOAP 客户端。本例中的 processor (1)和(2)是 InsertRequestOutHeaderProcessor 和 InsertResponseOutHeaderProcessor。我们的路由类似如下:

<route>
    <from uri="cxf:bean:routerRelayEndpointWithInsertion"/>
    <process ref="InsertRequestOutHeaderProcessor" />
    <to uri="cxf:bean:serviceRelayEndpointWithInsertion"/>
    <process ref="InsertResponseOutHeaderProcessor" />
</route>
Copy to Clipboard Toggle word wrap

在 2.x SOAP 标头中,会传播到 Apache Camel 消息标头。Apache Camel 消息标头名称为 org.apache.cxf.headers.Header.list,它是 CXF 中定义的常量(org.apache.cxf.headers.Header.HEADER_LIST)。标头值是 CXF SoapHeader 对象的 List <> (org.apache.cxf.binding.soap.SoapHeader)。以下代码片段是 InsertResponseOutHeaderProcessor (在响应消息中插入一个新的 SOAP 标头)。在 InsertResponseOutHeaderProcessorInsertRequestOutHeaderProcessor 中访问 SOAP 标头的方式实际上相同。两个处理器之间的唯一区别在于设置插入 SOAP 标头的方向。

public static class InsertResponseOutHeaderProcessor implements Processor {

    @SuppressWarnings("unchecked")
    public void process(Exchange exchange) throws Exception {
        // If exchange is routed from camel-cxf endpoint, this is the header
        List<SoapHeader> soapHeaders = CastUtils.cast((List<?>)exchange.getIn().getHeader(Header.HEADER_LIST));
        if (soapHeaders == null) {
            // we just create a new soap headers in case the header is null
            soapHeaders = new ArrayList<SoapHeader>();
        }

        // Insert a new header
        String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><outofbandHeader "
            + "xmlns=\"http://cxf.apache.org/outofband/Header\" hdrAttribute=\"testHdrAttribute\" "
            + "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" soap:mustUnderstand=\"1\">"
            + "<name>New_testOobHeader</name><value>New_testOobHeaderValue</value></outofbandHeader>";
        SoapHeader newHeader = new SoapHeader(soapHeaders.get(0).getName(),
                       DOMUtils.readXml(new StringReader(xml)).getDocumentElement());
        // make sure direction is OUT since it is a response message.
        newHeader.setDirection(Direction.DIRECTION_OUT);
        //newHeader.setMustUnderstand(false);
        soapHeaders.add(newHeader);

    }

}
Copy to Clipboard Toggle word wrap

如何在 PAYLOAD 模式中获取和设置 SOAP 标头

我们已显示了如何在 PAYLOAD 模式中访问 SOAP 消息(CxfPayload 对象) (请参阅 如何在 PAYLOAD 数据格式中处理 camel-cxf 端点的消息)。

获取 CxfPayload 对象后,您可以调用返回 DOM Elements (SOAP 标头)的 CxfPayload.getHeaders () 方法。

from(getRouterEndpointURI()).process(new Processor() {
    @SuppressWarnings("unchecked")
    public void process(Exchange exchange) throws Exception {
        CxfPayload<SoapHeader> payload = exchange.getIn().getBody(CxfPayload.class);
        List<Source> elements = payload.getBodySources();
        assertNotNull("Elements here", elements);
        assertEquals("Get the wrong elements size", 1, elements.size());

        Element el = new XmlConverter().toDOMElement(elements.get(0));
        elements.set(0, new DOMSource(el));
        assertEquals("Get the wrong namespace URI", "http://camel.apache.org/pizza/types",
                el.getNamespaceURI());

        List<SoapHeader> headers = payload.getHeaders();
        assertNotNull("Headers here", headers);
        assertEquals("Get the wrong headers size", headers.size(), 1);
        assertEquals("Get the wrong namespace URI",
                ((Element)(headers.get(0).getObject())).getNamespaceURI(),
                "http://camel.apache.org/pizza/types");
    }

})
.to(getServiceEndpointURI());
Copy to Clipboard Toggle word wrap

从 Camel 2.16.0 开始,您可以使用与 “如何在 POJO 模式中获取和设置 SOAP 标头”一节 中所述相同的方法设置或获取 SOAP 标头。现在,您可以使用 org.apache.cxf.headers.Header.list 标头来获取和设置 SOAP 标头列表。这意味着,如果您有一个路由从一个 Camel CXF 端点转发到另一个 Camel CXF 端点(SOAP Client Camel CXF 服务),则 SOAP 客户端发送的 SOAP 标头现在也会转发到 CXF 服务。如果您不希望转发标头,请从 org.apache.cxf.headers.Header.list Camel 标头中删除它们。

SOAP 标头在 MESSAGE 模式中不可用

SOAP 标头在 MESSAGE 模式中不可用,因为跳过 SOAP 处理。

如何从 Apache Camel 丢弃 SOAP Fault

如果您使用 CXF 端点来消耗 SOAP 请求,您可能需要从 camel 上下文中抛出 SOAP Fault。基本上,您可以使用 throwFault DSL 来执行此操作;它适用于 POJOPAYLOADMESSAGE 数据格式。您可以定义 soap 故障,如下所示:

SOAP_FAULT = new SoapFault(EXCEPTION_MESSAGE, SoapFault.FAULT_CODE_CLIENT);
Element detail = SOAP_FAULT.getOrCreateDetail();
Document doc = detail.getOwnerDocument();
Text tn = doc.createTextNode(DETAIL_TEXT);
detail.appendChild(tn);
Copy to Clipboard Toggle word wrap

然后像以下一样将其丢弃:

from(routerEndpointURI).setFaultBody(constant(SOAP_FAULT));
Copy to Clipboard Toggle word wrap

如果您的 CXF 端点以 MESSAGE 数据格式工作,您可以在消息正文中设置 SOAP Fault 消息,并在消息标头中设置响应代码。

from(routerEndpointURI).process(new Processor() {

    public void process(Exchange exchange) throws Exception {
        Message out = exchange.getOut();
        // Set the message body with the
        out.setBody(this.getClass().getResourceAsStream("SoapFaultMessage.xml"));
        // Set the response code here
        out.setHeader(org.apache.cxf.message.Message.RESPONSE_CODE, new Integer(500));
    }

});
Copy to Clipboard Toggle word wrap

POJO 数据格式也是如此。您可以在 Out 正文上设置 SOAP Fault,并通过调用 Message.setFault (true) 来指示它存在错误,如下所示:

from("direct:start").onException(SoapFault.class).maximumRedeliveries(0).handled(true)
    .process(new Processor() {
        public void process(Exchange exchange) throws Exception {
            SoapFault fault = exchange
                .getProperty(Exchange.EXCEPTION_CAUGHT, SoapFault.class);
            exchange.getOut().setFault(true);
            exchange.getOut().setBody(fault);
        }

    }).end().to(serviceURI);
Copy to Clipboard Toggle word wrap

如何传播 CXF 端点的请求和响应上下文

CXF 客户端 API 提供了使用请求和响应上下文调用操作的方法。如果您使用 CXF 端点制作者调用外部 Web 服务,您可以设置请求上下文并使用以下代码获取响应上下文:

        CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
             public void process(final Exchange exchange) {
                 final List<String> params = new ArrayList<String>();
                 params.add(TEST_MESSAGE);
                 // Set the request context to the inMessage
                 Map<String, Object> requestContext = new HashMap<String, Object>();
                 requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
                 exchange.getIn().setBody(params);
                 exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
                 exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
             }
         });
         org.apache.camel.Message out = exchange.getOut();
         // The output is an object array, the first element of the array is the return value
         Object\[\] output = out.getBody(Object\[\].class);
         LOG.info("Received output text: " + output\[0\]);
         // Get the response context form outMessage
         Map<String, Object> responseContext = CastUtils.cast((Map)out.getHeader(Client.RESPONSE_CONTEXT));
         assertNotNull(responseContext);
         assertEquals("Get the wrong wsdl opertion name", "{http://apache.org/hello_world_soap_http}greetMe",
                      responseContext.get("javax.xml.ws.wsdl.operation").toString());
Copy to Clipboard Toggle word wrap

附加支持

POJO 模式: 支持带有 Attachment 和 MTOM 的 SOAP (请参阅 Payload Mode for enable MTOM)。However, SOAP with Attachment is not tested.Since attachments is marshalled and unmarshalled to POJOs, users 通常不需要处理附件来附加它们。从 2.1Soo 开始,Camel 消息附加功能会被传播到 Camel 消息附加。

DataHandler Message.getAttachment(String id)
Copy to Clipboard Toggle word wrap

.

有效负载模式: 从 2.1 开始支持 MTOM。附件可以通过上述 Camel 消息 API 检索。不支持带有 Attachment 的 SOAP,因为此模式中没有 SOAP 处理。

要启用 MTOM,将 CXF 端点属性 "mtom_enabled" 设置为 true。(我认为您只能使用 Spring 执行此操作。)

<cxf:cxfEndpoint id="routerEndpoint" address="http://localhost:${CXFTestSupport.port1}/CxfMtomRouterPayloadModeTest/jaxws-mtom/hello"
         wsdlURL="mtom.wsdl"
         serviceName="ns:HelloService"
         endpointName="ns:HelloPort"
         xmlns:ns="http://apache.org/camel/cxf/mtom_feature">

     <cxf:properties>
         <!--  enable mtom by setting this property to true -->
         <entry key="mtom-enabled" value="true"/>

         <!--  set the camel-cxf endpoint data fromat to PAYLOAD mode -->
         <entry key="dataFormat" value="PAYLOAD"/>
     </cxf:properties>
Copy to Clipboard Toggle word wrap

您可以生成带有附件的 Camel 消息,以发送到 Payload 模式的 CXF 端点。

Exchange exchange = context.createProducerTemplate().send("direct:testEndpoint", new Processor() {

    public void process(Exchange exchange) throws Exception {
        exchange.setPattern(ExchangePattern.InOut);
        List&lt;Source> elements = new ArrayList&lt;Source>();
        elements.add(new DOMSource(DOMUtils.readXml(new StringReader(MtomTestHelper.REQ_MESSAGE)).getDocumentElement()));
        CxfPayload<SoapHeader> body = new CxfPayload<SoapHeader>(new ArrayList<SoapHeader>(),
            elements, null);
        exchange.getIn().setBody(body);
        exchange.getIn().addAttachment(MtomTestHelper.REQ_PHOTO_CID,
            new DataHandler(new ByteArrayDataSource(MtomTestHelper.REQ_PHOTO_DATA, "application/octet-stream")));

        exchange.getIn().addAttachment(MtomTestHelper.REQ_IMAGE_CID,
            new DataHandler(new ByteArrayDataSource(MtomTestHelper.requestJpeg, "image/jpeg")));

    }

});

// process response

CxfPayload<SoapHeader> out = exchange.getOut().getBody(CxfPayload.class);
Assert.assertEquals(1, out.getBody().size());

Map<String, String> ns = new HashMap<String, String>();
ns.put("ns", MtomTestHelper.SERVICE_TYPES_NS);
ns.put("xop", MtomTestHelper.XOP_NS);

XPathUtils xu = new XPathUtils(ns);
Element oute = new XmlConverter().toDOMElement(out.getBody().get(0));
Element ele = (Element)xu.getValue("//ns:DetailResponse/ns:photo/xop:Include", oute,
                                   XPathConstants.NODE);
String photoId = ele.getAttribute("href").substring(4); // skip "cid:"

ele = (Element)xu.getValue("//ns:DetailResponse/ns:image/xop:Include", oute,
                                   XPathConstants.NODE);
String imageId = ele.getAttribute("href").substring(4); // skip "cid:"

DataHandler dr = exchange.getOut().getAttachment(photoId);
Assert.assertEquals("application/octet-stream", dr.getContentType());
MtomTestHelper.assertEquals(MtomTestHelper.RESP_PHOTO_DATA, IOUtils.readBytesFromStream(dr.getInputStream()));

dr = exchange.getOut().getAttachment(imageId);
Assert.assertEquals("image/jpeg", dr.getContentType());

BufferedImage image = ImageIO.read(dr.getInputStream());
Assert.assertEquals(560, image.getWidth());
Assert.assertEquals(300, image.getHeight());
Copy to Clipboard Toggle word wrap

您还可以使用在 Payload 模式中从 CXF 端点接收的 Camel 消息。

public static class MyProcessor implements Processor {

    @SuppressWarnings("unchecked")
    public void process(Exchange exchange) throws Exception {
        CxfPayload<SoapHeader> in = exchange.getIn().getBody(CxfPayload.class);

        // verify request
        assertEquals(1, in.getBody().size());

        Map<String, String> ns = new HashMap<String, String>();
        ns.put("ns", MtomTestHelper.SERVICE_TYPES_NS);
        ns.put("xop", MtomTestHelper.XOP_NS);

        XPathUtils xu = new XPathUtils(ns);
        Element body = new XmlConverter().toDOMElement(in.getBody().get(0));
        Element ele = (Element)xu.getValue("//ns:Detail/ns:photo/xop:Include", body,
                                           XPathConstants.NODE);
        String photoId = ele.getAttribute("href").substring(4); // skip "cid:"
        assertEquals(MtomTestHelper.REQ_PHOTO_CID, photoId);

        ele = (Element)xu.getValue("//ns:Detail/ns:image/xop:Include", body,
                                           XPathConstants.NODE);
        String imageId = ele.getAttribute("href").substring(4); // skip "cid:"
        assertEquals(MtomTestHelper.REQ_IMAGE_CID, imageId);

        DataHandler dr = exchange.getIn().getAttachment(photoId);
        assertEquals("application/octet-stream", dr.getContentType());
        MtomTestHelper.assertEquals(MtomTestHelper.REQ_PHOTO_DATA, IOUtils.readBytesFromStream(dr.getInputStream()));

        dr = exchange.getIn().getAttachment(imageId);
        assertEquals("image/jpeg", dr.getContentType());
        MtomTestHelper.assertEquals(MtomTestHelper.requestJpeg, IOUtils.readBytesFromStream(dr.getInputStream()));

        // create response
        List&lt;Source> elements = new ArrayList&lt;Source>();
        elements.add(new DOMSource(DOMUtils.readXml(new StringReader(MtomTestHelper.RESP_MESSAGE)).getDocumentElement()));
        CxfPayload&lt;SoapHeader> sbody = new CxfPayload&lt;SoapHeader>(new ArrayList&lt;SoapHeader>(),
            elements, null);
        exchange.getOut().setBody(sbody);
        exchange.getOut().addAttachment(MtomTestHelper.RESP_PHOTO_CID,
            new DataHandler(new ByteArrayDataSource(MtomTestHelper.RESP_PHOTO_DATA, "application/octet-stream")));

        exchange.getOut().addAttachment(MtomTestHelper.RESP_IMAGE_CID,
            new DataHandler(new ByteArrayDataSource(MtomTestHelper.responseJpeg, "image/jpeg")));

    }
}
Copy to Clipboard Toggle word wrap

Message Mode: Attachments 不支持,因为它根本不处理消息。

CXF_MESSAGE 模式: 支持 MTOM,可以通过上述 Camel 消息 API 检索附件。

注意

当收到多部分(即 MTOM)消息时,默认的 SOAPMessageString converter 在正文上提供了完整的多部分有效负载。如果您只需要 SOAP XML 作为 String,您可以使用 message.getSOAPPart () 设置消息正文,Camel convert 可以为您执行其余工作。

如何传播堆栈追踪信息

可以配置 CXF 端点,以便在服务器端抛出 Java 异常时,异常的堆栈追踪会放入故障消息并返回到客户端。要启用此 feaure,请将 dataFormat 设置为 PAYLOAD,并将 cxfEndpoint 元素中的 faultStackTraceEnabled 属性设置为 true,如下所示:

<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage"
    wsdlURL="ship.wsdl"
    endpointName="s:TestSoapEndpoint"
    serviceName="s:TestService"
    xmlns:s="http://test">
  <cxf:properties>
    <!-- enable sending the stack trace back to client; the default value is false-->
    <entry key="faultStackTraceEnabled" value="true" /> <entry key="dataFormat" value="PAYLOAD" />
  </cxf:properties>
</cxf:cxfEndpoint>
Copy to Clipboard Toggle word wrap

出于安全考虑,堆栈追踪不包括导致的异常(即,被 导致的堆栈追踪的一部分)。如果要在堆栈追踪中包含导致异常,请在 cxfEndpoint 元素中将 exceptionMessageCauseEnabled 属性设置为 true,如下所示:

<cxf:cxfEndpoint id="router" address="http://localhost:9002/TestMessage"
    wsdlURL="ship.wsdl"
    endpointName="s:TestSoapEndpoint"
    serviceName="s:TestService"
    xmlns:s="http://test">
  <cxf:properties>
    <!-- enable to show the cause exception message and the default value is false -->
    <entry key="exceptionMessageCauseEnabled" value="true" />
    <!-- enable to send the stack trace back to client,  the default value is false-->
    <entry key="faultStackTraceEnabled" value="true" />
    <entry key="dataFormat" value="PAYLOAD" />
  </cxf:properties>
</cxf:cxfEndpoint>
Copy to Clipboard Toggle word wrap
警告

仅为测试和诊断目的启用 exceptionMessageCauseEnabled 标志。服务器正常做法是让服务器传达了最初的例外原因,使恶意用户更难以探测服务器。

PAYLOAD 模式中的流支持

在 2.8.2 中,camel-cxf 组件现在支持在使用 PAYLOAD 模式时传入的消息流。在以前的版本中,传入的信息会完全解析 DOM。对于大型消息,这非常耗时,并且使用了大量内存。从 2.8.2 开始,传入消息可以在路由时保留为 javax.xml.transform.Source,如果没有修改有效负载,则可以直接流传输到目标目的地。对于常见的"simple proxy"用例(例如: from ("cxf:…​").to ("cxf:…​"),这可能会造成非常显著的性能增加,并显著降低了内存要求。

然而,在有些情况下,流可能不合适或需要。由于流性质,在处理链中稍后之前,无效的传入 XML 可能无法被发现。此外,某些操作可能需要通过 DOM 解析消息,如 WS-Security 或消息追踪等,在这种情况下,流的优势有限。此时,可以通过两种方式控制流:

  • endpoint 属性:您可以添加 "allowStreaming=false" 作为端点属性,以打开流 on/off。
  • 组件属性:CxfComponent 对象也有一个 allowStreaming 属性,可以为从该组件创建的端点设置默认值。
  • 全局系统属性:您可以添加 org.apache.camel.component.cxf.streaming 的系统属性,以便在关闭时关闭。这会设置全局默认值,但设置上面的 endpoint 属性会覆盖该端点的值。

使用通用 CXF Dispatch 模式

从 2.8.0 开始,camel-cxf 组件支持通用 CXF 分配模式,它可以传输任意结构的消息(例如,不绑定到特定的 XML 模式)。要使用此模式,您可以省略指定 CXF 端点的 wsdlURL 和 serviceClass 属性。

<cxf:cxfEndpoint id="testEndpoint" address="http://localhost:9000/SoapContext/SoapAnyPort">
  <cxf:properties>
    <entry key="dataFormat" value="PAYLOAD"/>
  </cxf:properties>
</cxf:cxfEndpoint>
Copy to Clipboard Toggle word wrap

请注意,默认的 CXF 分配客户端不会发送特定的 SOAPAction 标头。因此,当目标服务需要特定的 SOAPAction 值时,会使用键 SOAPAction (不区分大小写)在 Camel 标头中提供。

78.1. WildFly 上的 CXF 用户

WildFly 上的 camel-cxf 用户配置与独立 Camel 的配置不同。生成者端点会按正常方式工作。

在 WildFly 上,camel-cxf 使用者利用容器提供的默认 Undertow HTTP 服务器。服务器在 undertow 子系统配置中定义。以下是 standalone.xml 的默认配置摘录:

<subsystem xmlns="urn:jboss:domain:undertow:4.0">
    <buffer-cache name="default" />
    <server name="default-server">
        <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true" />
        <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true" />
        <host name="default-host" alias="localhost">
            <location name="/" handler="welcome-content" />
            <filter-ref name="server-header" />
            <filter-ref name="x-powered-by-header" />
            <http-invoker security-realm="ApplicationRealm" />
        </host>
    </server>
</subsystem>
Copy to Clipboard Toggle word wrap

在本实例中,Undertow 配置为侦听由 http 和 https socket-binding 指定的接口 / 端口。默认情况下,这是 http 的端口 8080,https 为 8443。

例如,如果您使用不同主机或端口组合配置端点消费者,服务器日志文件中会出现一个警告。例如,以下主机和端口配置将被忽略:

<cxf:rsServer id="cxfRsConsumer"
              address="http://somehost:1234/path/to/resource"
              serviceClass="org.example.ServiceClass" />
Copy to Clipboard Toggle word wrap
<cxf:cxfEndpoint id="cxfWsConsumer"
                 address="http://somehost:1234/path/to/resource"
                 serviceClass="org.example.ServiceClass" />
Copy to Clipboard Toggle word wrap
[org.wildfly.extension.camel] (pool-2-thread-1) Ignoring configured host: http://somehost:1234/path/to/resource
Copy to Clipboard Toggle word wrap

但是,在默认主机和端口 localhost:8080 或 localhost:8443 上仍提供使用者。

注意

使用 camel-cxf 用户的应用程序必须 打包为 WAR。在之前的 WildFly-Camel 版本中,允许其他类型的存档,如 JAR,但这不再被支持。

78.1.1. 配置其他端口

如果接受其他端口,则必须通过 WildFly 子系统配置它们。这在服务器文档中解释:

https://access.redhat.com/documentation/zh-cn/red_hat_jboss_enterprise_application_platform/7.1/html/configuration_guide/configuring_the_web_server_undertow

78.1.2. 配置 SSL

要配置 SSL,请参阅 WildFly SSL 配置指南:

https://access.redhat.com/documentation/zh-cn/red_hat_jboss_enterprise_application_platform/7.1/html-single/how_to_configure_server_security/#configure_one_way_and_two_way_ssl_tls_for_application

78.1.3. 使用 Elytron 配置安全性

WildFly-Camel 支持使用 Elytron 安全框架保护 camel-cxf 消费者端点。

78.1.3.1. 配置安全域

要使用 Elytron 保护 WildFly-Camel 应用程序,需要在 WAR 部署的 WEB-INF/jboss-web.xml 中引用应用程序安全域:

<jboss-web>
  <security-domain>my-application-security-domain</security-domain>
</jboss-web>
Copy to Clipboard Toggle word wrap

& lt;security-domain& gt; 配置引用 Undertow 子系统定义的 < application-security-domain > 的名称。例如,Undertow 子系统 &lt ;application-security-domain > 在 WildFly 服务器 standalone.xml 配置文件中配置,如下所示:

<subsystem xmlns="urn:jboss:domain:undertow:6.0">
    ...
    <application-security-domains>
        <application-security-domain name="my-application-security-domain" http-authentication-factory="application-http-authentication"/>
    </application-security-domains>
</subsystem>
Copy to Clipboard Toggle word wrap

& lt;http-authentication-factory > application-http-authentication 在 Elytron 子系统中定义。在 standalone.xmlstandalone-full.xml 服务器配置文件中默认提供 application-http-authentication。例如:

<subsystem xmlns="urn:wildfly:elytron:1.2">
    ...
    <http>
        ...
        <http-authentication-factory name="application-http-authentication" http-server-mechanism-factory="global" security-domain="ApplicationDomain">
            <mechanism-configuration>
                <mechanism mechanism-name="BASIC">
                    <mechanism-realm realm-name="Application Realm" />
                </mechanism>
                <mechanism mechanism-name="FORM" />
            </mechanism-configuration>
        </http-authentication-factory>
        <provider-http-server-mechanism-factory name="global" />
    </http>
    ...
</subsystem>
Copy to Clipboard Toggle word wrap

名为 application -http-authentication 的 <http-authentication-factory > 包含对名为 ApplicationDomain 的 Elytron 安全域的引用。

有关如何配置 Elytron 子系统的更多信息,请参阅 Elytron 文档

camel-cxf 消费者端点的安全限制、身份验证方法和安全角色可以在 WAR 部署 WEB-INF/web.xml 中配置。例如,配置 BASIC 身份验证:

<web-app>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>secure</web-resource-name>
      <url-pattern>/webservices/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>my-role</role-name>
    </auth-constraint>
  </security-constraint>
  <security-role>
    <description>The role that is required to log in to /webservices/*</description>
    <role-name>my-role</role-name>
  </security-role>
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>my-realm</realm-name>
  </login-config>
</web-app>
Copy to Clipboard Toggle word wrap

请注意,Servlet 规范定义的 <url-pattern > 相对于 web 应用程序的上下文路径。如果您的应用程序被打包为 my-app.war,则 WildFly 将使其可在上下文路径 /my-app 下访问,< url-patternpattern& gt; /webservices jpeg 将应用到相对于 /my-app 的路径。

例如,针对 http://my-server/my-app/webservices/my-endpoint 的请求将与 /webservices>.&lt; pattern 匹配,而 http://my-server/webservices/my-endpoint 不匹配。

这很重要,因为 WildFly-Camel 允许创建 camel-cxf 端点消费者,其基本路径在主机 Web 应用上下文路径之外。例如,可以在 my-app.war 中为 http://my-server/webservices/my-endpoint 创建 camel-cxf 使用者。

为了为此类上下文端点定义安全限制,Wildly-Camel 支持自定义、非标准 < url-pattern& gt; 约定,其中前缀为三个正斜杠 /// / 将被解释为服务器主机名绝对。例如,要在 my-app.war 内保护 http://my-server/webservices/my-endpoint,您要将以下配置添加到 web.xml 中:

<web-app>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>secure</web-resource-name>
      <url-pattern>///webservices/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>my-role</role-name>
    </auth-constraint>
  </security-constraint>
  <security-role>
    <description>The role that is required to log in to /webservices/*</description>
    <role-name>my-role</role-name>
  </security-role>
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>my-realm</realm-name>
  </login-config>
</web-app>
Copy to Clipboard Toggle word wrap
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat