views:

715

answers:

3

A little up front info:

I have a SOAP service (hosted using JAX-WS (Endpoint class), but I don't think that is important).

I can connect to and use the web service just fine with Visual Studio generating the client (C#).

I generated a java client using Eclipse Web Tools (new --> other --> web services --> web services client).

Then I wrote a JUnit test to test the client. The test passes, but it takes an extremely long time to run. Each service call takes 300 seconds (give or take a couple seconds). Furthermore, it doesn't matter how fast the computer is. If I run this on my very slow work laptop it takes the same amount of time as if I run it on my fast home machine.

I have debugged into the axis code to the following function within org.apache.axis.encoding.DeserializationContext:

public void parse() throws SAXException
    {
        if (inputSource != null) {
            SAXParser parser = XMLUtils.getSAXParser();
            try {
                parser.setProperty("http://xml.org/sax/properties/lexical-handler", this);
                parser.parse(inputSource, this);

                try {
                    // cleanup - so that the parser can be reused.
                    parser.setProperty("http://xml.org/sax/properties/lexical-handler", nullLexicalHandler);
                } catch (Exception e){
                    // Ignore.
                }

no suprise, but the call to parser.parse() is taking up the 300 seconds. The from the web service is very short, so it shouldn't take much time to parse.

In case anyone is wondering, the actual type of parser is com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl

I can't debug into it because I don't have the source (and I'm tired up debugging 50 calls deep into commonly used libraries).

I'm currently running the profiler to include packages from Sun. I'll post my findings for that once it completes (adding all those packages significantly slows down the test)

I'm running Eclipse 3.5.1 and I'm using axis 1.4

Edit:

Here is the JUnit test:

@Test
public void testExecuter() throws IOException, InterruptedException, RemoteException, ServiceException
{
    //Listener l = new Listener(3456);
    //l.start();
    Executer exec = new ExecuterServiceLocator().getExecuterPort();
    //Executer exec = new ExecuterProxy("http://localhost:3456/Executer");
    System.out.println("executer created");
    _return remote = exec.execute("perl -e \"print 5\"", new EvAction[0]);
    System.out.println("after call 1");
    assertEquals("5", remote.getStdout());
    assertEquals("", remote.getStderr());
    assertEquals(0, remote.getReturnCode());
}

NOTE: Both ways of creating the Executer have the same thing happen

Edit2:

Here is the code I'm using to start the service:

public static void main(String[] args) {
    Endpoint.create(new Executer()).publish("http://localhost:3456/Executer");
}

I can't post the URL since I am just developping it on a single machine right now. However, here is the WSDL that is generated if I go to http://localhost:3456/Executer?WSDL

<!--
 Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6. 
-->
−
<!--
 Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6. 
-->
−
<definitions targetNamespace="http://executer/" name="ExecuterService">
−
<types>
−
<xsd:schema>
<xsd:import namespace="http://executer/" schemaLocation="http://localhost:3456/Executer?xsd=1"/&gt;
</xsd:schema>
</types>
−
<message name="Execute">
<part name="parameters" element="tns:Execute"/>
</message>
−
<message name="ExecuteResponse">
<part name="parameters" element="tns:ExecuteResponse"/>
</message>
−
<message name="IOException">
<part name="fault" element="tns:IOException"/>
</message>
−
<message name="InterruptedException">
<part name="fault" element="tns:InterruptedException"/>
</message>
−
<portType name="Executer">
−
<operation name="Execute">
<input message="tns:Execute"/>
<output message="tns:ExecuteResponse"/>
<fault message="tns:IOException" name="IOException"/>
<fault message="tns:InterruptedException" name="InterruptedException"/>
</operation>
</portType>
−
<binding name="ExecuterPortBinding" type="tns:Executer">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
−
<operation name="Execute">
<soap:operation soapAction=""/>
−
<input>
<soap:body use="literal"/>
</input>
−
<output>
<soap:body use="literal"/>
</output>
−
<fault name="IOException">
<soap:fault name="IOException" use="literal"/>
</fault>
−
<fault name="InterruptedException">
<soap:fault name="InterruptedException" use="literal"/>
</fault>
</operation>
</binding>
−
<service name="ExecuterService">
−
<port name="ExecuterPort" binding="tns:ExecuterPortBinding">
<soap:address location="http://localhost:3456/Executer"/&gt;
</port>
</service>
</definitions>

Edit:

I think this might be causing a problem:

I used TCPMonitor to look at the SOAP requests and I noticed that the client was speaking HTTP/1.0 and the server is speaking HTTP/1.1, but I don't know if this is causing the problem. I'm currently trying to figure out how to make the client speak HTTP/1.1.

Here are the SOAP messages in case anyone was wondering:

POST /Executer HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: USENBOONETL1C:2222
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 354

<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;&lt;soapenv:Body&gt;&lt;Execute xmlns="http://executer/"&gt;&lt;arg0 xmlns="">perl -e &quot;print 5&quot;</arg0></Execute></soapenv:Body></soapenv:Envelope>

and the response:

HTTP/1.1 200 OK
Content-type: text/xml;
charset="utf-8"
Content-length: 266

<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"&gt;&lt;S:Body&gt;&lt;ns2:ExecuteResponse xmlns:ns2="http://executer/"&gt;&lt;return&gt;&lt;stdout&gt;5&lt;/stdout&gt;&lt;stderr&gt;&lt;/stderr&gt;&lt;returnCode&gt;0&lt;/returnCode&gt;&lt;/return&gt;&lt;/ns2:ExecuteResponse&gt;&lt;/S:Body&gt;&lt;/S:Envelope&gt;

Edit:

Finally! turns out changing the HTTP client to CommonsHTTPClient and using HTTP/1.1 fixed the issue:

Here is the code I added to the client which fixed it:

BasicClientConfig basicClientConfig = new BasicClientConfig();
SimpleChain simpleChain = new SimpleChain();

simpleChain.addHandler(new CommonsHTTPSender());
basicClientConfig.deployTransport("http", simpleChain);

ExecuterServiceLocator l = new ExecuterServiceLocator(basicClientConfig);
...

note: You have to add common-httpclient.jar and common.codec.jar to the classpath.

A: 

Most likely a timeout. Could be that parser trying to download something from internet (DTD, XSD, etc) but you're behind a proxy/firewall.

Try to take stack trace of the server, when running the test to see what's going on (e.g. jps/ jstat).

maximdim
I've experienced this both at home and at work. Also, I'm pretty sure the server is not the problem because it is fast when a Visual Studio generated client calls it.
tster
+1  A: 

1) Verify that the C# and Java clients are sending exactly the same requests to the server. If you're using Jetty, turning on request logging would probably give you the data that you need.

2) Once the SAX parser starts running on the client, it's going to make callbacks that will wind up -- directly or indirectly -- invoking methods in your generated client. You should be able to set breakpoints at the beginning and end (and/or returns) of the generated client methods, and use these to determine where the delay[s] happen[s].

(BTW, in the SAX API, URLs such as http://xml.org/sax/properties/lexical-handler are used locally to identify property names; nothing is going to look for anything at that address. See http://xerces.apache.org/xerces2-j/properties.html for more info.)

Dan Breslau
This led me to the right answer. I updated my question to show how I fixed it in case anyone wonders.
tster
+1  A: 

Hi @tstr :)

Your problem is exactly what I'm facing now. And funny, I've gone through pretty much the same cycle (of the endpoint service, .net client working fine, and eclipse generated client taking a good 5 minutes to process). Thank you, indeed, for posting the updates and the solution. I'm not a java savvy, so may I ask if you applied the solution to the same eclipse generated client code? Or did you do something completely different? If you have modified the eclipse generated client code, where exactly have you added those lines?

Thanks, and and I really appreciate the help :)

Cheers, Arash

Arash
@Arash, The code that I posted to make it work is what you will put inside your code to instantiate the web service accessor. You don't need to change any generated code. Just where you actually go to call the service you need to add that http handler when you create ServiceLocator.
tster
BTW, if you appreciated the question, feel free to upvote ;)
tster
Wonderful :) Thank you so much. It works perfectly now. I will most certainly vote your question up as soon as my reputation reaches 15 :D
Arash