Jax-WS Axis2 Proxy over SSL error using ProxySelector
Asked Answered
S

1

9

In my project I have the following project structure:

I have a module that is producing a war file and can be deployed inside a Tomcat application server. This module has dependencies on Axis2 libraries:

<dependency>
        <groupId>org.apache.axis2</groupId>
        <artifactId>axis2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.axis2</groupId>
        <artifactId>axis2-transport-http</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.axis2</groupId>
        <artifactId>axis2-webapp</artifactId>
        <type>war</type>
    </dependency>

And this class contains an axis2.xml file in the conf folder under WEB-INF.

Now this module has a dependency on a unit module, that has the package type of a jar.

Now in my web-module, in the code for my stub I have following code:

GazelleObjectValidator.getInstance().validateObject();

The XcpdValidationService is a class in the jar module (dependency) and this method calls an external web service over SSL and using a proxy.

This web service client is generated by JAX WS RI

BUT this class doesn't use the axis2.xml configuration from the parent module and uses it's own axis configuration, being the default one, where my proxy is not configured...

@WebEndpoint(name = "GazelleObjectValidatorPort")
public GazelleObjectValidator getGazelleObjectValidatorPort() {
    return super.getPort(new QName("http://ws.validator.sch.gazelle.ihe.net/", "GazelleObjectValidatorPort"), GazelleObjectValidator.class);
}

The method itself looks like this:

@WebMethod
@WebResult(name = "validationResult", targetNamespace = "")
@RequestWrapper(localName = "validateObject", targetNamespace = "http://ws.validator.sch.gazelle.ihe.net/", className = "net.ihe.gazelle.schematron.ValidateObject")
@ResponseWrapper(localName = "validateObjectResponse", targetNamespace = "http://ws.validator.sch.gazelle.ihe.net/", className = "net.ihe.gazelle.schematron.ValidateObjectResponse")
public String validateObject(
    @WebParam(name = "base64ObjectToValidate", targetNamespace = "")
    String base64ObjectToValidate,
    @WebParam(name = "xmlReferencedStandard", targetNamespace = "")
    String xmlReferencedStandard,
    @WebParam(name = "xmlMetadata", targetNamespace = "")
    String xmlMetadata)
    throws SOAPException_Exception
;

My GazelleObjectValidatorService is generated by following plugin:

 <plugin>
     <groupId>org.apache.axis2</groupId>
     <artifactId>axis2-aar-maven-plugin</artifactId>
     <version>${axis2.version}</version>
     <extensions>true</extensions>
     <executions>
         <execution>
             <id>package-aar</id>
             <phase>prepare-package</phase>
             <goals>
                 <goal>aar</goal>
             </goals>
         </execution>
     </executions>
     <configuration>
         <fileSets>
             <fileSet>
             <directory>${project.basedir}/src/main/resources/wsdl</directory>
                 <outputDirectory>META-INF</outputDirectory>
                 <includes>
                     <include>**/*.xsd</include>
                 </includes>
             </fileSet>
         </fileSets>
         <servicesXmlFile>${project.build.outputDirectory}/axis2/services.xml</servicesXmlFile>
         <wsdlFile>${project.build.outputDirectory}/wsdl/ClientConnectorService.wsdl</wsdlFile>
    </configuration>
</plugin>

I tried to override the transportSender in my axis2.xml configuration with my own defined MyCommonsHttpTransportSender:

<transportSender name="http"
                 class="eu.epsos.pt.cc.MyCommonsHTTPTransportSender">
    <parameter name="PROTOCOL">HTTP/1.1</parameter>
    <parameter name="Transfer-Encoding">chunked</parameter>

and

<transportSender name="https"
                 class="eu.epsos.pt.cc.MyCommonsHTTPTransportSender">
    <parameter name="PROTOCOL">HTTP/1.1</parameter>
    <parameter name="Transfer-Encoding">chunked</parameter>
</transportSender>

that knows about the proxy.

but unfortunately since the web service client is inside the jar that is a dependency of the war, it doesn't seem to use my axis2.xml configuration, but uses it's own axis configuration, which doesn't know about the proxy.

This causes the following error where you see clearly that it uses the default CommonsHTTPTransportSender and therefore throwing the error:

Caused by: java.net.ConnectException: Connection refused (Connection refused)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.commons.httpclient.protocol.ReflectionSocketFactory.createSocket(ReflectionSocketFactory.java:140)
    at org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory.createSocket(SSLProtocolSocketFactory.java:130)
    at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
    at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$HttpConnectionAdapter.open(MultiThreadedHttpConnectionManager.java:1361)
    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
    at org.apache.axis2.transport.http.AbstractHTTPSender.executeMethod(AbstractHTTPSender.java:621)
    at org.apache.axis2.transport.http.HTTPSender.sendViaPost(HTTPSender.java:193)
    at org.apache.axis2.transport.http.HTTPSender.send(HTTPSender.java:75)
    at org.apache.axis2.transport.http.CommonsHTTPTransportSender.writeMessageWithCommons(CommonsHTTPTransportSender.java:404)
    at org.apache.axis2.transport.http.CommonsHTTPTransportSender.invoke(CommonsHTTPTransportSender.java:231)
    at org.apache.axis2.engine.AxisEngine.send(AxisEngine.java:443)
    at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:406)
    at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:229)
    at org.apache.axis2.client.OperationClient.execute(OperationClient.java:165)
    at org.apache.axis2.jaxws.core.controller.impl.AxisInvocationController.execute(AxisInvocationController.java:578)
    at org.apache.axis2.jaxws.core.controller.impl.AxisInvocationController.doInvoke(AxisInvocationController.java:127)
    at org.apache.axis2.jaxws.core.controller.impl.InvocationControllerImpl.invoke(InvocationControllerImpl.java:93)
    at org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler.invokeSEIMethod(JAXWSProxyHandler.java:373)
    ... 40 common frames omitted    

Is there a way to let the WS client in the child jar make use of the same axis2 configuration of the parent module (that is a deployable war and has the axis2 dependencies?)

UPDATE:

My WAR file has an axis2 configuration, from the source code of this war, a service generated with wsimport is called which is in a JAR that is a dependency of the parent WAR. This service calls an external WebService and this happens over Axis (although doesn't use the axis2.xml configuration file, since this one is in the WEB-INF folder of the JAR. Wouldn't there be any possibility to make the external WebService call in the JAR without Axis and use just JAXWS? This would solve my problems...

Serrato answered 30/8, 2017 at 15:41 Comment(5)
Are you looking to use your proxy selector or you simply need to configure the proxy for your stub?Hsining
I just want to know how I can configure the proxy for my stub. If it would be feasible using my proxy selector it would be great. If there is another way it's also a solution for me. For now he passes by the socket and just ignores my proxy selector since Axis is using the HTTP Client insideSerrato
I think I've found why: Axis isn't a true implementation of JAX-WS. Based on this previous answer of mine, we should be using one of the two approaches in there to set the secure socket factory (instead of the JAXWSProperties.SSL_SOCKET_FACTORY) in my answer. You should also delete your answer and add any further updates to the questionHsining
Thanks, but HttpsURLConnection.setDefaultSSLSocketFactory takes a SSLSocketFactory and not a SSLProtocolSocketFactorySerrato
See my updated answer. It was a mistake to assume Axis2 uses HTTPUrlConnectionHsining
H
3

Axis2 provides a convenient method to configure the HTTP Transport. So, following from your sample code:

HttpTransportProperties.ProxyProperties proxyProperties = new HttpTransportProperties.new ProxyProperties();
proxyProperties.setProxyHostName("hostName");
proxyProperties.setProxyPort("hostPort");
proxyProperties.setUsername("User");
proxyProperties.setPassword("pw");
//set the properties
objectValidatorService.getServiceClient().getOptions().setProperty(HttpConstants.PROXY, proxyProperties);

The above wouldn't work for you because you're using the stock JAX-WS implementation, not the Axis2-specific client. Based on your stacktrace, it appears you're connecting to a TLS-secured endpoint. There's a solution for that

I've done a lot of research, and there's no access to the underlying HTTPUrlConnection using stock JAX-WS. What we do have, is a way to set a custom SSLContextFactory. So we start by creating a custom factory, that will connect to the proxy first:

public class CustomSocketFactory extends SSLProtocolSocketFactory {

    private static final CustomSocketFactory factory = new CustomSocketFactory();

    static CustomSocketFactory getSocketFactory(){
        return factory;
    }     

    public CustomSocketFactory() {
        super();
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) {
        Socket socket = null;
        try {
           int proxyPort = 1000;
           InetSocketAddress proxyAddr = new InetSocketAddress("proxyAddr", proxyPort);
           Socket proxyConn = new Socket(new Proxy(Proxy.Type.SOCKS, proxyAddr));
           proxyConn.connect(new InetSocketAddress("endHost", 443));
           socket = (SSLSocket) super.createSocket(proxyConn, "proxyEndpoint", proxyPort, true);

        } catch (IOException ex) {

      Logger.getLogger(CustomSocketFactory.class.getName()).log(Level.SEVERE, null, ex);
    }
       return socket;
  }
}

we'll now register this custom socket factory with the Apache HTTPClient runtime (Axis does not use the stock java HTTPUrlConnection, as is evidenced by your stacktrace):

Protocol.registerProtocol("https",new Protocol("https", new CustomSocketFactory(), 443));

This works only for TLS connections. (although, a custom socket factory is applicable to non-https endpoints also). You also need to set the timeout to 0 so we can guarantee that your overriden createSocket gets invoked

Hsining answered 6/9, 2017 at 17:25 Comment(7)
Thank you for your answer. But unfortunately I don't have the getServiceClient() method on my GazelleObjectValidatorService. My GazelleObjectValidatorService is a class generated by JAX-WS RI 2.2.8Serrato
public class GazelleObjectValidatorService extends Service (javax.xml.ws.Service)Serrato
I added the configuration of my plugin used to generate the GazelleObjectValidatorSerrato
Thank you for your response. But how do you set the timeout to 0?Serrato
Use this @MathiasGhys. I've updated the code to use the port instead of the javax.xml.ws.Service classHsining
Thanks for your help: I updated my response with my latest code and error messagesSerrato
It's like Axis always interferes with web service requests instead of using the Custom Socket Factory. I also don't see a possibility to set the timeout...Serrato

© 2022 - 2024 — McMap. All rights reserved.