1
votes

I'm using Apache Camel to access to web service and got strange problem with invoking .NET web service.

That's the POST request sent by Camel:

ID: 1
Address: http://<domainName>/<pathToService>.asmx
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], breadcrumbId=[ID-someId-1449849054155-0-1], SOAPAction=["http://<someNamespace>/<SoapActionName>"]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>/soap:Body></soap:Envelope>

That's the response:

ID: 1
Response-Code: 302
Encoding: UTF-8
Content-Type: text/html; charset=utf-8
Headers: {Cache-Control=[private], Content-Length=[223], content-type=[text/html; charset=utf-8], Location=[/<somePath>/CustomError.htm?aspxerrorpath=/<pathToService>.asmx], Persistent-Auth=[true], Server=[Microsoft-IIS/7.5], X-AspNet-Version=[4.0.30319], X-Powered-By=[ASP.NET], X-UA-Compatible=[IE=7]}
Payload: <html><head><title>Object moved</title></head></html>

When I request "http://domainName/pathToService" in browser I see the correct answer.

When I request "http://domainName/pathToService.asmx?wsdl" I'm getting the wsdl file.

If I change SoapActionName to incorrect value I'm getting

"java.lang.IllegalArgumentException: Can't find the BindingOperationInfo with operation name ..." 

it's correct behavior too, of course.

I presume that I need to tune Camel to make it able to work with .NET web service.

But how?

Camel configuration (all in Spring DSL):

    <?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:cxf="http://camel.apache.org/schema/cxf"
   xmlns:http="http://cxf.apache.org/transports/http/configuration"
   xmlns:sec="http://cxf.apache.org/configuration/security"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd">

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>

<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>

<bean id="exceptionHandler" class="...ExceptionHandler"/>

<cxf:cxfEndpoint id="wsEndpoint"
                 address="${ws.host}/service.asmx"
                 serviceClass="pathToServiceClass"
                 loggingFeatureEnabled="true"
                 endpointName="endpointName">
    <cxf:outInterceptors>
        <ref bean="loggingOutInterceptor"/>
    </cxf:outInterceptors>
    <cxf:inInterceptors>
        <ref bean="loggingInInterceptor"/>
    </cxf:inInterceptors>
    <cxf:properties>
        <entry key="dataFormat" value="POJO"/>
    </cxf:properties>
</cxf:cxfEndpoint>

<http:conduit name="{serviceClass}endpointName.http-conduit">
    <http:authorization>
        <sec:UserName>domain\username</sec:UserName>
        <sec:Password>password</sec:Password>
        <sec:AuthorizationType>NTLM</sec:AuthorizationType>
    </http:authorization>
</http:conduit>

<camelContext xmlns="http://camel.apache.org/schema/spring">

    <route id="mainRoute">
        <from uri="file://inbox"/>
        <setHeader headerName="operationName">
            <constant>AcquireTicket</constant>
        </setHeader>
        <to uri="cxf:bean:wsEndpoint" pattern="InOut"/>
        <onException>
            <exception>java.lang.Exception</exception>
            <process ref="exceptionHandler"/>
        </onException>
        <to uri="log:mylog?Level=INFO"/>
    </route>
</camelContext>

I invoke Camel with JUnit:

    @Test
public void testWebservice() throws Exception {
    ... 
   forming request
   ...

    context.getRouteDefinition("mainRoute")
            .adviceWith(context, new AdviceWithRouteBuilder() {
                @Override
                public void configure() throws Exception {
                    replaceFromWith("direct:in");
                }
            });
    context.start();


    ProducerTemplate template = context.createProducerTemplate();
    template.sendBody("direct:in",request);
}

I tried to use request web service without Camel, with Apache HttpClient:

    DefaultHttpClient httpClient = new DefaultHttpClient();
    httpClient.getAuthSchemes().register(AuthPolicy.NTLM,new JCIFSNTLMSchemeFactory());
    httpClient.getAuthSchemes().register(AuthPolicy.SPNEGO,new JCIFSNTLMSchemeFactory());
    CredentialsProvider credsProvider = new BasicCredentialsProvider();
    NTCredentials ntCredentials = new NTCredentials(USERNAME, PASSWORD,
            InetAddress.getLocalHost().getHostName(), DOMAIN);
    HttpHost target = new HttpHost(HOST,80,"http");
    credsProvider.setCredentials(new AuthScope(target.getHostName(), target.getPort(),
            AuthScope.ANY_REALM, "NTLM"), ntCredentials);
    httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, ntCredentials);

    HttpPost post = new HttpPost(URL);

    post.addHeader("SOAPAction", SOAP_ACTION);
    post.addHeader("Content-Type",CONTENT_TYPE);
    post.addHeader("Accept-Encoding",ACCEPT_ENCODING);
    HttpEntity httpEntity = new StringEntity(getShortText());
    post.setEntity(httpEntity);

    HttpResponse response = httpClient.execute(post);

There is the last (3 of 3 NTLM requests) POST request created by Camel:

    POST /<pathToService>.asmx HTTP/1.1
    Content-Type: text/xml; charset=UTF-8
    Accept: */*
    SOAPAction:<someNamespace>/<SoapActionName>
    User-Agent: Apache CXF 3.1.4
    Cache-Control: no-cache
    Pragma: no-cache
    Host: localhost:8013
    Connection: keep-alive
    Content-Length: 269
    Authorization: NTLM <Token>

There is the last (3 of 3 NTLM requests) POST request created with the code above by Apache HttpClient:

    POST /<pathToService>.asmx HTTP/1.1
    Content-Type: text/xml; charset=utf-8
    Accept-Encoding: gzip,deflate
    SOAPAction:<someNamespace>/<SoapActionName>
    User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

    Host: localhost:8013
    Connection: Keep-Alive
    Content-Length: 269
    Authorization: NTLM <Token>

Both requests looks equals to me, but IIS response for the Camel request is HTTP 302 and IIS response for the Apache HttpCLient is HTTP 200.

Host is localhost:8013 because I use listener to analyze HTTP traffic, that listener forward all request to the remote server.

2

2 Answers

0
votes

Because your browser can follow 302 redirection from the location header.

The response you get is a HTTP 302 which is redirection based on the location header. So you need to tell Camel to follow 302 and get the URI from the location header.

0
votes

The solution:

  1. Add <http:client/> element to <http:conduit/>. My example:

    <http:conduit name="*.http-conduit">
        <http:client
            AllowChunking="false"
            MaxRetransmits="11"
            Connection="Keep-Alive"
            ReceiveTimeout="30000"
            CacheControl="No-Cache"
        />
        <http:authorization>
            <sec:UserName>domain\username</sec:UserName>
            <sec:Password>password</sec:Password>
            <sec:AuthorizationType>NTLM</sec:AuthorizationType>
        </http:authorization>
    </http:conduit>
    
  2. Force JRE to use Camel provided properties. My example:

2.1 Create class that changes the NTLM authorization option in constructor:

    public class NTLMCallbackHelper {

public NTLMCallbackHelper() {
    NTLMAuthenticationCallback.setNTLMAuthenticationCallback(
            new NTLMAuthenticationCallback () {
                @Override
                public boolean isTrustedSite(URL url) {
                    return false;
                }
            }
    );
}

2.2 Add bean to application context before any CXF beans:

<bean id = "NTLMCallbackHelper" class="my.package.NTLMCallbackHelper"/>

With this solution I got HTTP 200 response code from IIS.