Skip to main content

1) What is a "servicename" in jax-ws and 2) unable to parse valid XML

8 replies [Last post]
jsalvo
Offline
Joined: 2006-06-14

The webservice endpoint ( the server ) is not under my control, but it expects an XML document with a specific DTD, using HTTP. No WSDL.

I have the DTD(s), and I have "compiled" them to generate the Java classes using JAXB 2.0.

So these are all I have .... DTDs, and the generated Java classes from the DTDs.

I am able to marshall and unmarshall fine. When marshalling, I marshall to a StringWriter and writing the contents / buffer of the StringWriter to the http output stream ( using Jakarta HttpClient ), like so:

    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.setProperty( "jaxb.formatted.output", true );
    marshaller.marshal( svcInit, writer );

    // Using Jakarta-HttpClient
    HttpClient httpClient = new HttpClient();
    PostMethod method = new PostMethod( this.URL );
    method.setFollowRedirects(true);
    method.addRequestHeader("Content-Type", "text/xml; charset=UTF-8");
    method.setRequestBody( writer.toString() );
    httpClient.executeMethod( method );

    BufferedReader reader = new BufferedReader(
        new InputStreamReader( method.getResponseBodyAsStream()));
    StringBuffer buffer = new StringBuffer();
    String line = null;
    while( ( line = reader.readLine()) != null) {
      buffer.append( line );
      buffer.append( "\n" );
    }
    method.releaseConnection();
   
    System.out.println( "Response: \n" + buffer.toString() );
   
    JAXBContext jaxbResultContext=JAXBContext.newInstance(
        "org.openmobilealliance.mlp.result");
    Unmarshaller unmarshaller = jaxbResultContext.createUnmarshaller();
    Object obj = unmarshaller.unmarshal(
        new StreamSource( new StringReader( buffer.toString() ) ) );
   
    SvcResult svcResult = (SvcResult) obj;

The response / returned XML is also parsed correctly,
and the correct object is returned by the unmarshaller.

Next, I want to use a Service and a Dispatch instead of using HttpClient.

What I DON'T understand are:

1) What is the "servicename" that I must pass to "Service.create() " ? From the code below, it seems that I can actually use any name for the servicename. I could even use a QName with an empty name inside it!

2) What is the "portname" ? I can see that I can leave it null, and it works.

Here is the relevant piece of code, taken from an example that I saw on the web:

    String URL = "http://localhost:9210/LocationQueryService";

URI nsURI = new URI("");
    QName serviceName = new QName( "", nsURI.toString());

    Service s = Service.create( serviceName );
    URI address = new URI( this.URL );
   
    // A null portName works. What is it for then ????
    //s.addPort(portName, HTTPBinding.HTTP_BINDING, address.toString());
    s.addPort(null, HTTPBinding.HTTP_BINDING, address.toString());

    //Dispatch d = s.createDispatch(portName, jaxbContext,
        Service.Mode.MESSAGE);
    Dispatch d = s.createDispatch(null, jaxbContext,
        Service.Mode.MESSAGE);
    Map requestContext = d.getRequestContext();
    requestContext.put(MessageContext.HTTP_REQUEST_METHOD,
        new String("POST"));
    SvcResult svcResult = (SvcResult) d.invoke( svcInit );

Even though the call works, it THINKS that the XML being returned is invalid.
I say "it THINKS", because the returned XML __IS__ valid,
as it works if I use Jakarta HttpClient as shown above.

XML reader error: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[2,125]
Message: The markup declarations contained or pointed to by the document type declaration must be well-formed.
        at com.sun.xml.ws.encoding.xml.XMLMessage.create(XMLMessage.java:108)
        at com.sun.xml.ws.encoding.XMLHTTPCodec.decode(XMLHTTPCodec.java:143)
        at com.sun.xml.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:133)
        at com.sun.xml.ws.handler.HandlerPipe.process(HandlerPipe.java:107)
        at com.sun.xml.ws.client.Stub.process(Stub.java:121)
        at com.sun.xml.ws.client.dispatch.DispatchImpl.doInvoke(DispatchImpl.java:163)
        at com.sun.xml.ws.client.dispatch.DispatchImpl.invoke(DispatchImpl.java:189)
        at test.LocationRequestTestAsWebServiceClient.sendRequestAsWs(LocationRequestTestAsWebServiceClient.java:101)
        at test.LocationRequestTestAsWebServiceClient.testInvalidService(LocationRequestTestAsWebServiceClient.java:156)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at test.LocationRequestTestAsWebServiceClient.main(LocationRequestTestAsWebServiceClient.java:177)
Caused by: XML reader error: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[2,125]
Message: The markup declarations contained or pointed to by the document type declaration must be well-formed.
        at com.sun.xml.ws.streaming.XMLStreamReaderUtil.wrapException(XMLStreamReaderUtil.java:246)
        at com.sun.xml.ws.streaming.XMLStreamReaderUtil.next(XMLStreamReaderUtil.java:70)
        at com.sun.xml.ws.message.source.PayloadSourceMessage.(PayloadSourceMessage.java:58)
        at com.sun.xml.ws.message.source.PayloadSourceMessage.(PayloadSourceMessage.java:63)
        at com.sun.xml.ws.api.message.Messages.createUsingPayload(Messages.java:137)
        at com.sun.xml.ws.encoding.xml.XMLMessage.create(XMLMessage.java:100)
        ... 24 more

Here is the returned XML:

<?xml version="1.0" encoding="UTF-8"?>
%extension;]>
 
   
      46701000001
     
        20061116054704
       
         
           
              72 00 09N
              016 00 16E
           
            0
            500
            0
            120
         
       
     
   
 

It seems to be complaining about the semi-colon character in  ( %extension; ). But, like I said earlier, if I don't use Dispatch and use Jakarta HttpClient, everything works. Furthermore, if I copy and paste the XML to Eclipse, even eclipse says the XML document is valid.

Thus, I am ending up not using Dispatch, but using Jakarta HttpClient ( or even java.net.URL ) to send and receive the XML messages.

P.S. Note that the URL I am POSTing to is localhost. It is an emulator provided by the vendor.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
jsalvo
Offline
Joined: 2006-06-14

Ahh .. so that is what REST / RESTful I have been reading about but never bothered to find out what it is.

You have also explained why the error would occur ( JAXP vs. SJSXP ).

Is there also any benefit of using JAX-WS to create a webservice for this kind of thing, instead of just using a doing JAXB unmarshalling in a HttpServlet.doPost() ?

kohsuke
Offline
Joined: 2003-06-09

Well, I'm primarily a JAXB guy, so my opinion might be biased, but I usually find it easier (duh!) just to work with JAXB without JAX-WS.

JAX-WS gets really handy when you have WSDL and SOAP envelope and all, because you can let it worry about those.

jsalvo
Offline
Joined: 2006-06-14

OK .. Thanks for all your responses. It would be good though if JAXB marshalling and unmarshalling / binding was done by JAX-WS itself.

I have read that Axis2 has experimental support for JAXB binding as part of its REST support, but can't figure out write a service if I have already have the Java beans generated from JAXB

kohsuke
Offline
Joined: 2003-06-09

JAX-WS does the JAXB unmarshalling/marshalling. Maybe I misunderstood your question.

jsalvo
Offline
Joined: 2006-06-14

Thank you again for responding.

The problem that I have is that, all the documentation about JAX-WS talks about setting up a webservice from a WSDL. I have not seen how you can setup JAX-WS to setup a webservice from a DTD.

http://java.sun.com/webservices/docs/2.0/tutorial/doc/

Thus, what I ended up doing was creating a servlet, and within that servlet's doPost() method, I do:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
JAXBContext jaxbContext=JAXBContext.newInstance("org.openmobilealliance.mlp.request");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object obj1 = unmarshaller.unmarshal( request.getInputStream() );

SvcInit svcInit = (SvcInit) obj1;

< ..... snip ... >
}

Would have been good if you can create a webservice from DTD just as you would from a WSDL, thereby removing the need for the me, the developer, to code in the unmarshalling and marshalling call via JAXB.

kohsuke
Offline
Joined: 2003-06-09

Well, you have to admit that no one does web services with DTD...

If you are basically doing REST-style web service like that, then you can use Dispatch with a JAXBContext you created (from classes you compiled from DTD)

kohsuke
Offline
Joined: 2003-06-09

What you are doing is called "REST" in this part of the technology world, just so you know.

If what you described me is the kind of thing you do, then I don't think JAX-WS buys you that much. You can just use JAXB with java.net.URL (I didn't understand why you needed Jakarta HTTP client when URL should do.)

But since you asked, to answer your questions...

1. serviceName and portName are WSDL concepts. If you are using JAX-WS for REST, they are meaningless.

2. as for the parsing error, I suspect the problem is that those system IDs in your DTD are failing to resolve (but in any case I agree that the error message is rather poor), or maybe it's really a bug in the parser. When you use JAXB like that, you are using JAXP SAX parser. When you use JAX-WS Like that, you are using SJSXP. So they go through rather different code path.

kohsuke
Offline
Joined: 2003-06-09

I filed https://sjsxp.dev.java.net/issues/show_bug.cgi?id=11 for SJSXP to improve error messages.