1

I have code that uses Apache CXF 3.1.18 and Java 1.7. It makes a SOAP call to a third party service to create tickets. CXF Generated my client stubs from the services' WSDL. The group managing the service now wants to enable "Require WS-Security header verification for all incoming SOAP requests" which also sets "Require authorization for incoming WSDL requests" settings. My job is to make my code compatible with the service with these settings enabled. See below for a listing of the communication class.

The code below works when the two security settings are disabled, and it generates tickets in the service's system. Enabling security generates the first stack trace shown below; adding the Authenticator makes the code go a little bit further, but I'm stuck trying to decipher "javax.xml.ws.soap.SOAPFaultException: Invalid QName in mapping: wsse:InvalidSecurity" and move forward with solving my problem.

package com.myCompany.service.webservice.client;

import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;

import com.myCompany.instrumentation.Instrumentation;
import com.myCompany.logging.Log;

/* Note: these next three classes are generated by CXF from the WSDL */
import com.myCompany.service.ServiceTicket;
import com.myCompany.service.ServiceTicketManager;
import com.myCompany.service.ServiceTicketManagerSoap;

import com.myCompany.myProject.biz.TicketTO;

public class ServiceWebServiceClientImpl implements
        ServiceWebServiceClient {
    private TicketTO uTicket;
    private ServiceTicket serviceTicket;
    private List<TeamGroupTO> result;

    private static Log log = Instrumentation.getSmLog( "ServiceClient");

    private static String SERVICE_WSDL ="https://myCompanyDev.service.com/ServiceTicketManager.do?WSDL";
    private static String SERVICE_USERNAME="myServiceUser";
    private static String SERVICE_PASSWORD="mYsERVICEpASSWORD";
    private static String SERVICE_SERVICE_NAME="ServiceTicketManager";
    private static String SERVICE_URL="http://www.service.com";

    static{
        try {
            SERVICE_WSDL = /* get from configuration */;
            SERVICE_USERNAME=/* get from configuration */;
            SERVICE_PASSWORD=/* get from configuration */;
            SERVICE_SERVICE_NAME=/* get from configuration */;
            SERVICE_URL=/* get from configuration */;
        } catch (Exception e) {
            log.warn( e.toString(), e);
        }
    }

    // Main entry point here
    @Override
    public TicketTO createTicket( TicketTO ticket) {
        uTicket = ticket;
        ServiceTicketManagerSoap port = getPort();

        ServiceTicket serviceTicket = 
                createServiceTicket( ticket);

        Holder<ServiceTicket> serviceTicket_holder = 
                new Holder<ServiceTicket>( serviceTicket);

        /* This is Line 2, that throws the Invalid QName exception */
        port.createTicket(serviceTicket_holder);

        serviceTicket = serviceTicket_holder.value;

        uTicket.setSvcTicketId(serviceTicket.getId());
        uTicket.setSvcTicketNumber(serviceTicket.getNumber());

        return uTicket;
    }

    private static ServiceTicketManagerSoap getPort() {

        /* Code Block 1 location */

        URL wsdlURL = ServiceTicketManager.WSDL_LOCATION;
        try {
            wsdlURL = new URL(ServiceWebServiceClientImpl.SERVICE_WSDL);
        } catch (MalformedURLException e) {
            log.warn( e.toString(), e);
        }

        QName qname = new QName(ServiceWebServiceClientImpl.SERVICE_URL, 
                ServiceWebServiceClientImpl.SERVICE_SERVICE_NAME);

        /* This is Line 1, that throws the 401 Exception */
        ServiceTicketManager ss = new ServiceTicketManager( wsdlURL, qname);

        ServiceTicketManagerSoap port = ss.getServiceTicketManagerSoap();

        Client client = ClientProxy.getClient( port); 
        HTTPConduit http = (HTTPConduit) client.getConduit();
        http.getAuthorization().setUserName( ServiceWebServiceClientImpl.SERVICE_USERNAME);
        http.getAuthorization().setPassword( ServiceWebServiceClientImpl.SERVICE_PASSWORD);

        BindingProvider portBP = (BindingProvider) port;
        Map<String, Object> requestContext = portBP.getRequestContext();

        requestContext.put( BindingProvider.USERNAME_PROPERTY, SERVICE_USERNAME);
        requestContext.put( BindingProvider.PASSWORD_PROPERTY, SERVICE_PASSWORD);

        return port;
    }

    private ServiceTicket createServiceTicket( TicketTO ticket) 
    {
        // TicketTOOld.EnumUserTypes.Reporter
        ServiceTicket serviceTicket = 
                new ServiceTicket();
        // ...

        return serviceTicket;
    }
}

I found this page 401 error when consuming a Web service with HTTP Basic authentication using CXF, which suggested I add this code:

    Authenticator.setDefault( new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(
                ServiceWebServiceClientImpl.SERVICE_USERNAME,
                ServiceWebServiceClientImpl.SERVICE_PASSWORD.toCharArray());
        }
    });

at the location noted below as /* Code Block 1 location */.

With this addition, the code gets past line 1, but throws an "Invalid QName in mapping" exception where I've noted as line 2. See Stack Trace #2.

I also found this page How to add Basic Authorization to a wsdl on startup with cxf? but I'm not sure how I'm supposed to work the given solution into my code. I don't really have enough "big picture" knowledge of how CXF works to translate the API calls made on that page into what my code given above is doing.

My search skills have so far not been up to the task. My personal goal is to understand more about the architecture of CXF and how the java.net.Authenticator class fits into the whole picture. But I've been coming up short looking for the architecture of how this all works, rather than the man pages for the individual classes.

This code was put together and gotten working by searching for problems fairly quickly by some talented contractors, but I have no idea whether it should or could be simplified.

I've already downloaded the WSDL, and used it to generate the client stubs. Is there a way to tell the code to use a local copy of the WSDL instead of downloading it?

Stack Trace #1:

javax.xml.ws.WebServiceException: org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
    at org.apache.cxf.jaxws.ServiceImpl.initialize(ServiceImpl.java:162)
    at org.apache.cxf.jaxws.ServiceImpl.<init>(ServiceImpl.java:129)
    at org.apache.cxf.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:82)
    at javax.xml.ws.Service.<init>(Service.java:77)
    at com.myCompany.myProject.service.ServiceTicketManager.<init>(ServiceTicketManager.java:43)
    at com.myCompany.myProject.service.webservice.client.ServiceWebServiceClientImpl.getPort(ServiceWebServiceClientImpl.java:109)
    at com.myCompany.myProject.service.webservice.client.ServiceWebServiceClientImpl.createIncidentTicket(ServiceWebServiceClientImpl.java:77)
    at com.myCompany.myProject.webstar.biz.CreateIncidentManager_Test.createIncident(CreateIncidentManager_Test.java:79)
    ...
Caused by: org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
    at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:87)
    at org.apache.cxf.jaxws.ServiceImpl.initializePorts(ServiceImpl.java:217)
    at org.apache.cxf.jaxws.ServiceImpl.initialize(ServiceImpl.java:160)
    ... 34 more
Caused by: javax.wsdl.WSDLException: WSDLException: faultCode=PARSER_ERROR: Problem parsing 'https://mycompanyinstance.service.com/ServiceTicketManager.do?WSDL'.: java.io.IOException: Server returned HTTP response code: 401 for URL: https://mycompanyinstance.service.com/ServiceTicketManager.do?WSDL
    at com.ibm.wsdl.xml.WSDLReaderImpl.getDocument(WSDLReaderImpl.java:2198)
    at com.ibm.wsdl.xml.WSDLReaderImpl.readWSDL(WSDLReaderImpl.java:2390)
    at com.ibm.wsdl.xml.WSDLReaderImpl.readWSDL(WSDLReaderImpl.java:2422)
    at org.apache.cxf.wsdl11.WSDLManagerImpl.loadDefinition(WSDLManagerImpl.java:290)
    at org.apache.cxf.wsdl11.WSDLManagerImpl.getDefinition(WSDLManagerImpl.java:181)
    at org.apache.cxf.wsdl11.WSDLServiceFactory.<init>(WSDLServiceFactory.java:85)
    ... 36 more
Caused by: java.io.IOException: Server returned HTTP response code: 401 for URL: https://mycompanyinstance.service.com/ServiceTicketManager.do?WSDL
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1694)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263)
    at org.apache.xerces.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
    at org.apache.xerces.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
    at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
    at com.ibm.wsdl.xml.WSDLReaderImpl.getDocument(WSDLReaderImpl.java:2188)
    ... 41 more

Stack Trace #2:

javax.xml.ws.soap.SOAPFaultException: Invalid QName in mapping: wsse:InvalidSecurity
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:161)
    at com.sun.proxy.$Proxy38.createTicket(Unknown Source)
    at com.myCompany.myProject.service.webservice.client.ServiceWebServiceClientImpl.createTicketTicket(ServiceWebServiceClientImpl.java:nn (port.createTicket, Line 2 noted in the code)
    at com.myCompany.myProject.webstar.biz.CreateTicketManager_Test.createTicket(CreateTicketManager_Test.java:73)
...
Caused by: java.lang.RuntimeException: Invalid QName in mapping: wsse:InvalidSecurity
    at org.apache.cxf.staxutils.StaxUtils.readQName(StaxUtils.java:1863)
    at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.unmarshalFault(Soap11FaultInInterceptor.java:65)
    at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:52)
    at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:41)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:309)
    at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:112)
    at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
    at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:309)
    at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:825)
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1689)
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1565)
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1366)
    at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
    at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:663)
    at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:309)
    at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:523)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:432)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:347)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:305)
    at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:139)
    ... 30 more
Chris Wood
  • 55
  • 2
  • 13
  • were you able to find solution to this? I was able to work with java.net.Authenticator but same authentication is used for all calls. Is there solution from Apache CXF itself? – NiTiN Oct 29 '20 at 14:20
  • I was not able to find a solution, so the group that controlled the server withdrew their request to enable the additional security. – Chris Wood Oct 30 '20 at 15:05

0 Answers0