Apache CXF SOAP - 处理 MTOM 附件:响应是意外的 text/html ContentType

Apache CXF SOAP - handling MTOM attachment: Response was of unexpected text/html ContentType

我有一个使用 Apache CXF 生成的 SOAP 客户端,但处理 MTOM 附件时出现问题:

WARNING: Interceptor for {http://someaddress.com.au/soap2/}
SomeAddressPortTypeService#{http://someaddress.com.au/soap2/}
SomeFunction has thrown exception, unwinding now 
org.apache.cxf.interceptor.Fault: Response was of unexpected text/html ContentType.    
Incoming portion of HTML stream: <?xml version="1.0" encoding="UTF-8"?>
....etc

下面是我的pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>au.com.someid</groupId>
    <artifactId>SomeSOAPClient</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>SomeSOAPClient</name>
    <url>http://maven.apache.org</url>
    <properties>
        <junit.version>4.12</junit.version>
        <cxf.version>3.0.4</cxf.version>
        <spring.version>4.1.7.RELEASE</spring.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${cxf.version}</version>
         </dependency>
         <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf.version}</version>
         </dependency>
     </dependencies>

     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <version>3.5.1</version>
                 </configuration>
             </plugin>

             <plugin>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <configuration>
                     <descriptorRefs>
                         <descriptorRef>jar-with-dependencies</descriptorRef>
                     </descriptorRefs>
                 </configuration>
             </plugin>

             <plugin>
                 <groupId>org.apache.cxf</groupId>
                 <artifactId>cxf-codegen-plugin</artifactId>
                 <version>${cxf.version}</version>
                 <executions>
                     <execution>
                     <id>generate-sources</id>
                     <phase>generate-sources</phase>
                     <configuration>
                         <sourceRoot>src/main/java-generated</sourceRoot>
                             <wsdlOptions>
                                 <wsdlOption>
                            <wsdl>http://localhost:4001/someAddress.WSDL</wsdl>
                                      <extraargs>
                                          <extraarg>-autoNameResolution</extraarg>
                                     </extraargs>
                                 </wsdlOption>
                             </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

</project>

下面是我的beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:jaxws="http://cxf.apache.org/jaxws" 
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd
    http://cxf.apache.org/jaxws 
    http://cxf.apache.org/schemas/jaxws.xsd
    ">
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<context:property-placeholder/>
<context:annotation-config/>
<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"/>

<jaxws:client id="soapClientBean"
    address="http://localhost:4001"
    serviceClass="some.package.SomePortType">
</jaxws:client>

<context:component-scan base-package="some.package, 
    another.package"/>      

客户端 class 定义如下:

@Component
public class SOAPClient {

    @Autowired
    private SomePortType soapClientBean;

    public String doStuff(String arg, ...){ ... }

最后测试类似于下面:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:beans.xml"})
public class SOAPClientTest {

@Autowired
SOAPClient soapClient;

@Test
public void testDoStuff(){
    assertNotNull(soapClient.doStuff("blah"));
}

我的理解是可以在 CXF 客户端上添加一个拦截器来重置 Message.CONTENT_TYPE 属性,但我不知道该怎么做。

或者有别的方法吗?

有人可以帮忙吗?谢谢!

更新:

我也试过如下启用 MTOM,但没有效果:

Binding binding = ((BindingProvider) soapClientBean).getBinding();
((SOAPBinding)binding).setMTOMEnabled(true);

更新2:

此外,根据 http://cxf.apache.org/docs/simple-frontend-configuration.html,下面的配置也应该启用 MTOM,但它也没有任何效果(添加命名空间 & 并重新定义客户端 bean,如下所示):

xmlns:simple="http://cxf.apache.org/simple"
xmlns:soap="http://cxf.apache.org/bindings/soap"


 <simple:client id="soapClientBean"
    address="http://localhost:4001"
    serviceClass="some.package.SomePortType">
    <simple:binding>
        <soap:soapBinding mtomEnabled="true" version="1.2"/>    
    </simple:binding>
</simple:client>

原来要走的路是:
启用 mtom 并注入拦截器:

 <bean id="inInterceptor" class="some.package.InInt"/>
 <bean id="inFaultInterceptor" class="some.package.InFaultInt"/>

 etc...

 <jaxws:client id="soapClientBean"
    address="http://localhost:4001"
    serviceClass="some.package.SomePortType">
    <jaxws:binding>
        <soap:soapBinding mtomEnabled="true" version="1.2" />   
    </jaxws:binding>
    <jaxws:inInterceptors>
        <ref bean="inInterceptor"/>
    </jaxws:inInterceptors>
    <jaxws:inFaultInterceptors>
        <ref bean="inFaultInterceptor"/>
    </jaxws:inFaultInterceptors>
</jaxws:client> 

etc...

拦截器的定义类似于:

public class InInt extends AbstractSoapInterceptor{

private SAAJInInterceptor saajInterceptor = new SAAJInInterceptor();
public InInt(String p) {
    super(p);
    // TODO Auto-generated constructor stub
}
public InInt() {
      super(Phase.PRE_PROTOCOL);
      getAfter().add(SAAJInInterceptor.class.getName());
   }

@Override
public void handleMessage(SoapMessage message) throws Fault {
    SOAPMessage doc = message.getContent(SOAPMessage.class);
    if (doc == null) {
         saajInterceptor.handleMessage(message);
         doc = message.getContent(SOAPMessage.class);
      }
      SOAPHeader header = null;
      try {
         header = doc.getSOAPHeader();
      } catch (SOAPException e) {
        // exception handling logic...
      }

      //header handling logic here...

      SOAPBody body = null;
      try {
          body = doc.getSOAPBody();
      } catch (SOAPException e){
          //exception handling logic...
      }
      //body handling logic here...
}