5

I have the WSDL file for the SOAP webservice that i need to invoke over http. Using cxf wsdl2java plugin i have created the stub methods.

I have created the webservice client using jaxws. The webservice has basic authentication enabled. I am trying to configure http conduit

 my application.properties
 --------------------------
 webservices.http.auth.username=username
 webservices.http.auth.password=password
 fold.webservices.http.auth.authtype=Basic
 webservices.http.conduit.property.name=https://fixed_deposits-test.co.in/fold-webservices/services.*
 fold.updateservice.soap.address=https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService
 ----------------------------     

My Spring Context...

 <?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:http-conf="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://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
                    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

   <bean id="properties" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
      <property name="locations">
        <util:list>
            <value>file:${config.dir}/application.properties</value>
        </util:list>
      </property>
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
   </bean>

   <jaxws:client id="updateServiceClient" serviceClass="com.fold.facade.v1.UpdateService" address="${fold.updateservice.soap.address}" >
      <jaxws:inInterceptors>
        <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" >
            <property name="prettyLogging" value="true" />
        </bean>
      </jaxws:inInterceptors>
      <jaxws:outInterceptors>
        <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" >
            <property name="prettyLogging" value="true" />
        </bean>
      </jaxws:outInterceptors>
   </jaxws:client>

  <http-conf:conduit name="***?????????***">
    <http-conf:authorization>
        <sec:UserName>${fold.webservices.http.auth.username}</sec:UserName>
        <sec:Password>${fold.webservices.http.auth.password}</sec:Password>
        <sec:AuthorizationType>${fold.webservices.http.auth.authtype}</sec:AuthorizationType>
    </http-conf:authorization>
</http-conf:conduit>

I have done a lot of searching online so as to what should be the valid value for name attribute..

accouring to CXF documentation it should be...

{WSDL_endpoint_target_namespace}PortName.http-conduit

my WSDL File has..

...
targetNamespace="http://facade.fold.com/" and
...
<wsdl:port binding="tns:UpdateServiceImplServiceSoapBinding"
        name="UpdateServiceImplPort">
        <soap:address
            location="https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService" />
    </wsdl:port>

so i tried with these..

<http-conf:conduit name="{http://facade.fold.com/}UpdateServiceImplPort.http_conduit">
or
<http-conf:conduit name="*UpdateServiceImplPort.http_conduit">
or
<http-conf:conduit name="{http://facade.fold.com/}*.http_conduit">

But none of them work as i get 401 unauthorized exception..

org.apache.cxf.transport.http.HTTPException: HTTP response '401: Unauthorized' when communicating with https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService

THERE ARE COUPLE OF WAYS I GOT IT TO WORK

a) <http-conf:conduit name="*.http_conduit">

but i really don't want to do it this way...

b) <http-conf:conduit name="https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService">

this is hardcoding the SOAP service URL... which i don't want as i am looking for externalizing URL as my SOAP URL's are different for different environment..(dev /test /prod etc)

Below is my best shot at externalization, but it failed with 401 Unauthorized Exception...

properties were replaced in all other instances in my spring context, but not for http-conf:conduit name attribute :(

<http-conf:conduit name="${webservices.http.conduit.property.name}">

As a workaround i am currently using the regex approach which works..

<http-conf:conduit name="*.*/fold-webservices/services/UpdateService">

But i really want to figure out if it possible to externalize it and read from properties file. And i want to do it the Spring configuration way. Please help me !!!

Manjunath
  • 141
  • 2
  • 7

3 Answers3

3

We had the same issue with JBoss Fuse 6.1 (Camel CXF 2.12.0). You can see what the http-conf:conduit name is set to by enabling DEBUG log level and looking at your log, there should be a log line such as:

DEBUG 10:40:41,437 [Camel (cnpms-as-mnp-ctx) thread #0 - JmsConsumer[cnpms-queue]] org.apache.cxf.transport.http.HTTPConduit.setTlsClientParameters(HTTPConduit.java:901) - Conduit '{http://acme.com/manageporting/v1}ManageportingPortPort.http-conduit' has been (re)configured for plain http.

So in this case you would set the name as:

<http-conf:conduit name="{http://acme.com/manageporting/v1}ManageportingPortPort.http-conduit"

But the Web Service (WS) Interface Class is defined as:

@WebService(targetNamespace = "http://acme.com/manageporting/v1", name = "ManageportingPort")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface ManageportingPort {

Generated from WSDL:

<wsdl:portType name="ManageportingPort">

Note that by following the CXF documentation you would expect the port name component to be "ManageportingPort" NOT "ManageportingPortPort" i.e. with "Port" appended to it. However looking at how the portQName is resolved in org.apache.cxf.jaxws.support.JaxWsImplementorInfo.getEndpointName(), if the port name is not set in the WS Interface Class, and the name is not null or blank, it sets the port name to portName = name + "Port" otherwise it sets it to portName = implementorClass.getSimpleName() + "Port".

Tim
  • 98
  • 5
  • 1
    +1 for the DEBUG hint. The expected conduit name was not what I was expecting based on my WSDL. Saves you a lot of trial and error. – Jaap Oct 31 '16 at 15:15
2

I had the same problem...

In my case the following 2 changes helped:

1) add "Port" postfix to the port name, despite it is not defined in the wsdl this was

e.g wsdl: 
<wsdl:port name="XXXSoap">

will be "XXXSoapPort" in the conduit definition

2) remove the "\" at the end of the target namespace name

==> therefore try

<http-conf:conduit name="{http://facade.fold.com}UpdateServiceImplPort.http_conduit">
or 
<http-conf:conduit name="{http://facade.fold.com}UpdateServiceImplPortPort.http_conduit">
Lemi
  • 21
  • 4
1

I came across the same challenge and found no existing solution. Spring doesn't seem to resolve placeholders in bean names (make lots of sense). However, this is a valid case unless cxf allows conduit matching using another attribute.

There are a few ways to solve this problem:

  1. Define conduit beans programmatically (lose the neat of xml declaration)
  2. Find a way to resolve the bean names which contains placeholders

I prefer option 1 and this is the implementation which I'm happy with. Please note that PropertyPlaceholderResolver is our own utility which uses the same defined resolver bean.

public class NameWithPlaceholderBeanFactoryPostProcessor implements BeanFactoryPostProcessor
{
    @Override
    public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        if (!(beanFactory instanceof DefaultListableBeanFactory))
            return;

        DefaultListableBeanFactory bf = (DefaultListableBeanFactory) beanFactory;

        String[] names = bf.getBeanDefinitionNames();
        for (String name : names)
        {
            if (name.indexOf('$') < 0)
                continue;

            BeanDefinition bd = bf.getBeanDefinition(name);
            bf.removeBeanDefinition(name);
            bf.registerBeanDefinition(PropertyPlaceholderResolver.resolvePlaceHolders(name), bd);
        }
    }
}

The final step is to define this as a spring bean.

Community
  • 1
  • 1
t7tran
  • 1,899
  • 1
  • 18
  • 16