搜索

第 77 章 CXF

download PDF

CXF 组件

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>
注意

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

注意

在流处理模式中使用 CXF 时(请参阅 DataFormat 选项),然后读取 流缓存

Camel on EAP 部署

该组件受到 EAP (Wildfly Camel)框架的 Camel 支持,该框架在红帽 JBoss 企业应用平台(JBoss EAP)容器上提供简化的部署模型。

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

注意

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

URI 格式

cxf:bean:cxfEndpoint[?options]

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

cxf://someAddress[?options]

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

对于以上任意样式,您可以在 URI 中附加选项,如下所示:

cxf:bean:cxfEndpoint?wsdlURL=wsdl/hello_world.wsdl&dataFormat=PAYLOAD

选项

名称

必需

描述

wsdlURL

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

file://local/wsdl/hello.wsdlwsdl/hello.wsdl

serviceClass

SEI (服务端点接口)类的名称。此类可以具有,但不需要 JSR181 注解。  自 2.0 起, 只有 POJO 模式需要这个选项。如果提供了 wsdlURL 选项,则 PAYLOAD 和 MESSAGE 模式不需要 serviceClass。当在没有 serviceClass 的情况下使用 wsdlURL 选项时,会提供 serviceName 和 portName (Spring configuration 的endpointName)选项 MUST

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

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

由于 2.8, 可以省略 PAYLOAD 和 MESSAGE 模式的 wsdlURL 和 serviceClass 选项。省略它们时,可以在 PAYLOAD 模式中将任意 XML 元素放在 CxfPayload 的正文中,以促进 CXF Dispatch 模式。

例如: org.apache.camel.Hello

serviceName

只有在 WSDL 中存在多个 serviceName

此服务名称实施,它将映射到 wsdl:service@name。例如:

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

endpointName

只有在 serviceName 下存在多个 portName 时,且从 camel 2.2 开始需要 camel-cxf consumer。

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

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

dataFormat

消息数据格式支持 CXF 端点。可能的值有: POJO (默认), PAYLOAD,MESSAGE.

relayHeaders

请参见此选项 的"转发标题 "选项部分。应该将 CXF 端点转发标头与路由一起。目前,只有 dataFormat=POJO默认:true示例true,false时才可用

wrapped

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

wrappedStyle

2.5.0 开始,WSDL 风格描述 SOAP 正文中的参数如何表示。如果值为 false,CXF 将选择文档号未换行样式,如果值为 true,则 CXF 将选择 document-literal wrapped 样式

setDefaultBus

deprecated: 指定是否为此端点使用默认的 CXF 总线。可能的值有: true,false (默认)这个选项已弃用,您应该从 Camel 2.16 开始 使用默认的Bus

defaultBus

deprecated: 指定是否为此端点使用默认的 CXF 总线。可能的值有: true,false (默认)这个选项已弃用,您应该从 Camel 2.16 开始 使用默认的Bus

bus

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

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

cxfBinding

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

headerFilterStrategy

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

loggingFeatureEnabled

在 2.3 中新功能,这个选项启用 CXF Logging 功能来写入入站和出站 SOAP 信息日志。可能的值有: true,false (默认)

defaultOperationName

在 2.4 中新增,此选项将设置默认的 operationName,它将由调用远程服务的 CxfProducer 使用。例如:

defaultOperationName=greetMe

defaultOperationNamespace

在 2.4 中新增,此选项将设置默认的 operationNamespace,它将由调用远程服务的 CxfProducer 使用。例如:

defaultOperationNamespace=http://apache.org/hello_world_soap_http

synchronous

2.5 中的新功能,此选项将使 CXF 端点决定使用同步或 async API 进行底层工作。默认值为 false,这意味着 camel-cxf 端点将默认尝试使用 async API。

publishedEndpointUrl

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

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 中的新功能.此选项控制 CXF 组件在 PAYLOAD 模式下运行时(参见下面的),DOM 会将传入的消息解析为 DOM Elements 中,或者将有效负载保留为允许在某些情况下允许流传输的 javax.xml.transform.Source 对象。

skipFaultLogging

2.11 中的新功能.这个选项控制阶段拦截器链是否跳过记录它所捕获的故障。

cxfEndpointConfigurer

Camel 2.11 中的新功能.这个选项可以应用 org.apache.camel.component.cxfEndpointConfigurer 的实现, 它支持以编程方式配置 CXF 端点。自 Camel 2.15.0 起, 用户可以通过实施 CxfEndpointConfigurer 的配置{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 仅将 continuation timeout 设置为 0,这表示连续暂停操作超时。)

默认 : 30000 示例 : continuation=80000

serviceNameportName s 为 QNames,因此如果您提供它们,请务必将它们前缀为 {namespace},如上例所示。

数据格式的描述

DataFormat

描述

POJO

POJO (说明旧 Java 对象)是目标服务器上被调用的方法的 Java 参数。支持协议和逻辑 JAX-WS 处理程序。

有效负载

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

MESSAGE

MESSAGE 是从传输层接收的原始消息。如果您不假定要 touch 或 change Stream,如果您使用这类 DataFormat 时,将删除某些 CXF 拦截器,以便在 camel-cxf consumer 和 JAX-WS 处理程序后不会显示任何 soap 标头。

CXF_MESSAGE

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

您可以通过检索交换属性 CamelCXFDataFormat 来确定交换的数据模型。Exchange key constant 在 org.apache.camel.component.cxfConstants.DATA_FORMAT_PROPERTY 中定义。

使用 Apache Aries Blueprint 配置 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>

目前,endpoint 元素是第一个支持的 CXF 命名空间handler。

您还可以使用 bean 参考,就像在 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: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>

如何在 MESSAGE 模式中启用 CXF 的 LoggingOutInterceptor

CXF 的 LoggingOutInterceptor 输出出站消息,该消息位于线上可记录系统(java.util.logging)。由于 LoggingOutInterceptor 处于 PRE_STREAM 阶段(但 PRE_STREAM 阶段在 MESSAGE 模式下),因此您必须在 WRITE 阶段配置 LoggingOutInterceptor 来运行。以下是一个示例。

   <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>

转发标头选项描述

从 JAXWS WSDL 优先开发人员的角度,存在带外 和带线标头。

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

带外标头是通过 线序列化的标头,但没有明确属于 WSDL 绑定合同的一部分。

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

当路由具有 CXF 端点且开发人员需要有线头头(如 SOAP 标头)时,该路由会与另一个 JAXWS 端点所消耗的路由一起 转发,然后转发标头 应设置为 true (这是默认值)。

仅在 POJO 模式中可用

转发Headers=true 设置代表转发标头的意向。对给定标头进行转发的实际决定是否被委派给了实施 MessageHeadersRelay 接口的可插拔实例。将咨询一个对 MessageHeadersRelay 的整合实施,以确定是否需要转发标题。已有一个 SoapMessageHeadersRelay 的实现,它将自身绑定到知名的 SOAP 命名空间。目前,只过滤掉带外的标头,且在转发 Headers=true 时,始终会转发 带外的标头。如果线上有一个标头,则该命名空间对于运行时未知,则会使用一个回退 DefaultMessageHeadersRelay,只允许所有标头被转发。

转发Headers=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"/>

查看演示如何在这里转发/drop 标头的测试:

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 模式或插入)CXF 拦截器/JAXWS Handler 到 CXF 端点。
  • Message Header Relay 机制已合并到 CxfHeaderFilterStrategy 中。转发Headers 选项、其语义和默认值保持不变,但它是 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>

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

    <route>
        <from uri="cxf:bean:routerNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
        <to uri="cxf:bean:serviceNoRelayEndpoint?headerFilterStrategy=#dropAllMessageHeadersStrategy"/>
    </route>
  • 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>
  • 除了 转发标头 外,在 CxfHeaderFilterStrategy 中可以配置新的属性。

名称

描述

type

必需?

默认值

relayHeaders

所有邮件标题将由 Message Header Filters 处理

布尔值

true (1.6.1 behavior)

relayAllMessageHeaders

将传播所有邮件标题(不需要由消息标头过滤器处理)

布尔值

false (1.6.1 behavior)

allowFilterNamespaceClash

如果两个过滤器在激活命名空间中重叠,则属性控制应如何处理它。如果值为 true,则最后一个胜过。如果值为 false,它将抛出异常

布尔值

false (1.6.1 behavior)

使用 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     ">
 ...
注意

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

cxf:cxfEndpoint 元素支持很多附加属性:

名称

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 注解。

它还支持许多子元素:

名称

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>列表

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>

如何使 camel-cxf 组件使用 log4j 而不是 java.util.logging

CXF 的默认日志记录器是 java.util.logging。如果要将其更改为 log4j,请按如下操作:在 classpath 中创建一个文件,取名为 META-INF/cxf/org.apache.cxf.logger。此文件应当包含类的完全限定域名 org.apache.cxf.common.Log4jLogger 在单个行中。

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

如果您使用一些 SOAP 客户端,如 PHP,则会收到此类错误,因为 CXF 中没有添加 XML start 文档 < ?xml version="1.0" 编码="utf-8"?>

Error:sendSms: SoapFault exception: [Client] looks like we got no XML document in [...]

要解决这个问题,您只需要告诉 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);
    }

}

您可以添加一个客户拦截器,将其配置为 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 will force 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>

如果您使用的是 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);

如何使用 POJO 数据格式来自 camel-cxf 端点的消息

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});
    }

}

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

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

如果您没有在消息标头中指定操作名称,则 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 will be filled in the reset of List.
// The result will be 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("We should get the response context here", "UTF-8", responseContext.get(org.apache.cxf.message.Message.ENCODING));
assertEquals("Reply body on Camel is wrong", "echo " + TEST_MESSAGE, result.get(0));

如何在 PAYLOAD 数据格式处理 camel-cxf 端点的信息

在 Apache Camel 2.0: CxfMessage.getBody () 中,将返回 org.apache.camel.component.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);
                }
            });
        }
    };
}

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

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

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

<route>
    <from uri="cxf:bean:routerRelayEndpointWithInsertion"/>
    <process ref="InsertRequestOutHeaderProcessor" />
    <to uri="cxf:bean:serviceRelayEndpointWithInsertion"/>
    <process ref="InsertResponseOutHeaderProcessor" />
</route>

在 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 {
        // You should be able to get the header if exchange is routed from camel-cxf endpoint
        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);

    }

}

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

我们已介绍如何在 PAYLOAD 模式中访问 SOAP 消息(CxfPayload 对象) (请参阅 “如何在 PAYLOAD 数据格式处理 camel-cxf 端点的信息”一节)。

获取 CxfPayload 对象后,您可以调用返回 DOM Elements (SOAP headers)的 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("We should get the 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("We should get the 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());

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

SOAP 标头在 MESSAGE 模式中不可用

当 SOAP 处理被跳过时,SOAP 标头在 MESSAGE 模式中不可用。

如何从 Apache Camel 抛出 SOAP 错误

如果您使用 CXF 端点来消耗 SOAP 请求,您可能需要丢弃来自 camel 上下文的 SOAP 错误。基本上,您可以使用 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);

然后像您一样进行丢弃:

from(routerEndpointURI).setFaultBody(constant(SOAP_FAULT));

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

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));
    }

});

POJO 数据格式也是如此。您可以在 Out body 上设置 SOAP 失败,并通过调用 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);

如何传播 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());

附加支持

POJO 模式: 支持带 Attachment 和 MTOM 的双 SOAP (请参阅启用 MTOM 的 Payload Mode)。 但是,带有 Attachment 的 SOAP 没有被测试。 由于附件进行了汇总,并且未编入 POJO,因此用户通常无需处理他们自己附加的附件。 Attachments 被传播到从 2.1 开始的 Camel 消息的附件。 因此,可以通过 Camel 消息 API 重新发送附件

DataHandler Message.getAttachment(String id)

.

有效负载模式: 自 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>

您可以使用附加生成 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());

您还可以在 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")));

    }
}

Message Mode: 不支持连接,因为它并不处理消息。

CXF_MESSAGE 模式: 支持 MTOM,并且可按照上述 Camel 消息 API 检索附件。请注意,当收到多部分(即 MTOM)消息时,默认的 SOAPMessage to String converter 将在正文中提供完整的多部分有效负载。如果您只要求 SOAP XML 作为字符串,您可以使用 message.getSOAPPart () 设置消息正文,Camel 转换可以为您进行其余工作。

如何传播堆栈追踪信息

可以配置 CXF 端点,以便在服务器端引发 Java 异常时,异常堆栈的追踪会被整合到容错消息中,并返回给客户端。要启用此功能,请将 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>

出于安全考虑,堆栈跟踪不包括导致的异常(也就是说,堆栈跟踪的一部分 如果要在堆栈追踪中包含导致异常,请在 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>
警告

对于测试和诊断目的,您应该只启用 exceptionMessageCauseEnabled 标志。服务器通常的做法是原始原因(例外),使恶意用户更难以探测服务器。

PAYLOAD 模式流支持

在 2.8.2 中,Carl-cxf 组件现在支持在使用 PAYLOAD 模式时,支持在传入消息流处理。在以前的版本中,传入的信息已被完全解析。对于大型消息,这需要消耗大量内存。从 2.8.2 开始,传入消息可在路由期间保持为 javax.xml.transform.Source,如果不修改有效负载,则可以直接流传输到目标目的地。对于常见的"简单代理"用例(例如: from ("cxf:…​").to ("cxf:…​")),这可以提供非常显著的性能增加,以及显著降低的内存要求。

然而,在有些情况下,流性可能不适合或需要。由于流性,在稍后处理链中不会发现无效的传入 XML。另外,某些操作可能需要下载消息,如 WS-Security 或消息追踪(例如,流处理的优点)。此时,可以通过两种方式控制流:

  • endpoint 属性:您可以添加 "allowStreaming=false" 作为端点属性,以打开/关闭流。
  • component 属性:CxfComponent 对象也有一个 allowStreaming 属性,可为从该组件创建的端点设置默认值。
  • 全局系统属性:您可以将 "org.apache.camel.component.cxf.streaming" 的系统属性添加到 "false",以关闭 if。这将设置全局默认值,但上方设置 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>

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

77.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>

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

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

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

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

注意

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

77.1.1. 配置备用端口

如果需要接受其他端口,则必须通过 WildFly 子系统配置来配置这些端口。这在服务器文档中阐述:

https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.1/html/configuration_guide/configuring_the_web_server_undertow

77.1.2. 配置 SSL

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

https://access.redhat.com/documentation/en-us/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

77.1.3. 使用 Elytron 配置安全性

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

77.1.3.1. 配置安全域

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

<jboss-web>
  <security-domain>my-application-security-domain</security-domain>
</jboss-web>

& 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>

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

<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>

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

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

77.1.3.2. 配置安全约束、验证方法和安全角色

camel-cxf consumer 端点的安全约束、身份验证方法和安全角色可以在您的 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>

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

例如,针对 http://my-server/my-app/webservices/my-endpoint 的请求将匹配 /webservices/* 模式,而 http://my-server/webservices/my-endpoint 不会匹配。

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

为了为此类 非上下文端点定义安全约束,Wild-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>
Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

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

让开源更具包容性

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

關於紅帽

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

© 2024 Red Hat, Inc.