Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jax-WS Axis2 Proxy over SSL error using ProxySelector

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...

like image 885
Mathias G. Avatar asked Aug 30 '17 15:08

Mathias G.


1 Answers

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

like image 67
kolossus Avatar answered Oct 22 '22 06:10

kolossus