Interoperability between .NET and Java via Web Services – Apache Axis JWS

Pre-requisites: <?xml:namespace prefix = o ns = “urn:schemas-microsoft-com:office:office” />/o:p

JDK 1.4+/o:p

Web Server (Apache Tomcat in this case)/o:p

Apache Axis 1.2 http://ws.apache.org/axis/ /o:p

.NET Framework 1.1/o:p

 /o:p

Apache Axis is a Web Services library available in Java and C++, developed as an open-source project under the auspices of the Apache foundation. Although there are some related Apache projects implementing WS-* specifications I’m just looking at Axis basics (if and when .NET developers need to talk to Java Web Service developers) and also inter-op between the two Web Service stacks. /o:p

/o:p 

[Disclaimer: I’m not a java guy. I’m not a Web Services expert. I’m just trying to document what I’ve found. If you think something I’ve written here is incorrect please leave a comment or send me an email and I’ll investigate & update this.]/o:p

 /o:p

Installation/o:p

The Axis installation instructions do a good job of explaining how to set up Axis, and the happiness page http://localhost:8080/axis/happyaxis.jsp gives you pretty good feedback as to what is missing in your Axis install. I’ve used Axis RC2 for the purposes of this investigation, and will try and update when the final version is released./o:p

 /o:p

Two Flavors of Web Services/o:p

 /o:p

Apache Axis supports two different flavors of Web Services – JWS, and “custom” deployment with deployment descriptors (an XML config file, what else) used throughout the J2EE world for configuring deployable units./o:p

 /o:p

JWS – The simplest thing that could possibly work/o:p

 /o:p

Once your Axis environment is set up, JWS is about as simple as Web Services could get. You re-name a Java class file to .jws, copy it into your Axis directory and when a request is made an end-point is compiled on the fly, exposing all the public methods of the class as port types (as if you’d decorated every public method of a class with the [WebMethod] attribute in ASP.NET Web Services). I’m sure the “schema first” people in the Java world like this kind of Web Service development about as much as they like having pointy things thrust into there eye./o:p

 /o:p

The StockQuote JWS example file that ships with Axis has the following Java code:/o:p

 /o:p

public class StockQuoteService {/o:p

  public float getQuote (String symbol) throws Exception {/o:p

     // code omitted for brevity/o:p

  }/o:p

}/o:p

 /o:p

Which gets exposed something like this:/o:p

 /o:p

<?<xml version=1.0 encoding=UTF-8?>/o:p

<wsdl:definitions targetNamespace=http://localhost:8181/axis/StockQuoteService.jws xmlns:apachesoap=http://xml.apache.org/xml-soap xmlns:impl=http://localhost:8181/axis/StockQuoteService.jws xmlns:intf=http://localhost:8181/axis/StockQuoteService.jws xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/ xmlns:wsdl=http://schemas.xmlsoap.org/wsdl/ xmlns:wsdlsoap=http://schemas.xmlsoap.org/wsdl/soap/ xmlns:xsd=http://www.w3.org/2001/XMLSchema>/o:p

    /o:p

    <wsdl:message name=getQuoteRequest>/o:p

        <wsdl:part name=symbol type=xsd:string/>/o:p

    </</< SPAN></< SPAN>wsdl:message>/o:p

    <wsdl:message name=getQuoteResponse>/o:p

        <wsdl:part name=getQuoteReturn type=xsd:float/>/o:p

    </</< SPAN></< SPAN>wsdl:message>/o:p

    <wsdl:portType name=StockQuoteService>/o:p

        <wsdl:operation name=getQuote parameterOrder=symbol>/o:p

            <wsdl:input message=impl:getQuoteRequest name=getQuoteRequest/>/o:p

            <wsdl:output message=impl:getQuoteResponse name=getQuoteResponse/>/o:p

        </</< SPAN></< SPAN>wsdl:operation>/o:p

    </</< SPAN></< SPAN>wsdl:portType>/o:p

    <wsdl:binding name=StockQuoteServiceSoapBinding type=impl:StockQuoteService>/o:p

        <wsdlsoap:binding style=rpc transport=http://schemas.xmlsoap.org/soap/http/>/o:p

        <wsdl:operation name=getQuote>/o:p

            <wsdlsoap:operation soapAction=””/>/o:p

            <wsdl:input name=getQuoteRequest>/o:p

                <wsdlsoap:body encodingStyle=http://schemas.xmlsoap.org/soap/encoding/ namespace=http://DefaultNamespace use=encoded/>/o:p

            <</< SPAN>wsdl:input>/o:p

            <wsdl:output name=getQuoteResponse>/o:p

                <wsdlsoap:body encodingStyle=http://schemas.xmlsoap.org/soap/encoding/ namespace=http://localhost:8181/axis/StockQuoteService.jws use=encoded/>/o:p

            </</< SPAN></< SPAN>wsdl:output>/o:p

        </</< SPAN></< SPAN>wsdl:operation>/o:p

    </</< SPAN></< SPAN>wsdl:binding>/o:p

    <wsdl:service name=StockQuoteServiceService>/o:p

        <wsdl:port binding=impl:StockQuoteServiceSoapBinding name=StockQuoteService>/o:p

            <wsdlsoap:address location=http://localhost:8181/axis/StockQuoteService.jws/>/o:p

        </</< SPAN></< SPAN>wsdl:port>/o:p

    </</< SPAN></< SPAN>wsdl:service>/o:p

</< SPAN></< FONT></wsdl:definitions>/o:p

 /o:p

/o:p

/o:p

This is a little different to the sort of schema you would see if the [WebMethod] attribute was added to an equivalent .NET method. In particular the <?xml:namespace prefix = wsdl />wsdl:typeselement (shown below from an equivalent .NET service) is not present in the Axis JWS WSDL. /o:p

<wsdl:types>/o:p

    <s:schema elementFormDefault=qualified targetNamespace=http://tempuri.org/>/o:p

      <s:element name=GetQuote>/o:p

        <s:complexType>/o:p

          <s:sequence>/o:p

            <s:element minOccurs=0 maxOccurs=1 name=symbol type=s:string />/o:p

          </</< SPAN></< SPAN>s:sequence>/o:p

        </</< SPAN></< SPAN>s:complexType>/o:p

      </</< SPAN></< SPAN>s:element>/o:p

      <s:element name=GetQuoteResponse>/o:p

        <s:complexType>/o:p

          <s:sequence>/o:p

            <s:element minOccurs=1 maxOccurs=1 name=GetQuoteResult type=s:float />/o:p

          </</< SPAN></< SPAN>s:sequence>/o:p

        </</< SPAN></< SPAN>s:complexType>/o:p

      </</< SPAN></< SPAN>s:element>/o:p

    </</< SPAN></< SPAN>s:schema>/o:p

  </</< SPAN></< SPAN>wsdl:types>/o:p

 /o:p

By default .NET services are document-style services which “wrap” simple schema types in complex types for each message. The Axis service is an RPC-style service. In .NET we could create an equivalent RPC-style service by applying the [SoapRpcMethod] attribute to a single method, or the [SoapRpcService] to our entire service. At this time there doesn’t seem to be any way of configuring Axis JWS services to be anything other than RPC-literal (there is an open enhancement here http://issues.apache.org/jira/browse/AXIS-1393 to make this configurable). While the WS-I does permit both document and RPC style services as part of the basic profile, document is considered a more explicit super-set of RPC./o:p

/o:p

/o:p/o:p

 /o:p

In order to test this very simple service I created a C# windows forms project and added a webreference. 2 lines of code later I was pulling stock quotes back from my .jws Axis Web Service without any problems (and people say this whole “integration” thing is hard)./o:p

 /o:p

When the endpoint is first requested Axis generates a .java file in the %AXISROOT%WEB-INF/jwsClasses directory on the fly which it then complies./o:p

 /o:p

Returning Complex Types/o:p

 /o:p

Impressed with my progress I tried to write my own JWS to return a complex type Customer. I attempted to do this using the following Java code:/o:p

 /o:p

public class CustomerInfoService {/o:p

  public Customer getCustomer (int id) throws Exception {/o:p

    Customer cust = new Customer();/o:p

    cust.name = “Test Customer”;/o:p

    cust.id = id;/o:p

    return cust;/o:p

  } /o:p

}/o:p

 /o:p

class Customer/o:p

{/o:p

      public String name;/o:p

      public int id;/o:p

}/o:p

 /o:p

I was able to call the getCustomer method using the following console application in the same package/o:p

 /o:p

public class Caller{/o:p

  public static void main(String[] args){/o:p

      CustomerInfoService svc = new CustomerInfoService();/o:p

      try{/o:p

            System.out.println(svc.getCustomer(1).name);/o:p

      }/o:p

      catch (Exception ex){/o:p

            System.out.println(“caught” + ex.getMessage());/o:p

      }/o:p

  }/o:p

}/o:p

 /o:p

However when I attempted to create this as an Axis JWS Web Service I got the following error: /o:p

 /o:p

Exception - java.lang.NoClassDefFoundError: Customer

 /o:p

Creating the Customer type as an inner class to CustomerInfoService, and qualifying the return type didn’t work either (it did work from the console application). /o:p

 /o:p

Exception - java.lang.NoClassDefFoundError: CustomerInfoService$Customer

 /o:p

Strangely enough, Java .class files WERE being created for these types in the jwsClass directory (where Axis does its code generation and compilation for JWS to work). Hmm….There’s something funny going on here./o:p

 /o:p

A Serialization Problem? Web Services? Well, knock me over with a feather!/o:p

A possible explanation for the problems serializing complex types using Axis-JWS is that JWS can’t serialize/de-serialize the complex type Customer that I’m trying to return. Although the Axis docs don’t go into specifics about complex type serialization in JWS, the descriptor-based method of deployment does discuss serialization. In descriptor-based Axis Web Services you gain a great deal more control about how your endpoint is exposed, as well as how types are serialized. The BeanSerializer will handler bean-style classes with getProperty and setProperty methods, but you do have to hook this up in your descriptor, so I think it’s fair to say that in JWS Axis Web Services this is (probably) not enabled by default. Although not explicitly stated in the documentation some discussions from the Axis mailing list back this up./o:p

 /o:p

http://marc.theaimsgroup.com/?l=axis-user&m=101907793104693&w=2 /o:p

http://marc.theaimsgroup.com/?l=axis-user&m=107037683118922&w=2 /o:p

 /o:p

My good friend Java Pete thought there were some possible ways to work around these issues by registering a global serialization handler (and that just MIGHT work), however this (if it did work) was more of a dirty hack than a viable solution./o:p

/o:p 

An exceptional JWS Service/o:p

Since returning complex types appeared to be something of a dead-end, I started looking at some other interoperability considerations. I created the following service to test the exception propagation facilities between the Axis Web Service and .NET/o:p

 /o:p

public class ExceptionalService{/o:p

      public void RaiseException() throws Exception{/o:p

            throw new Exception();/o:p

      }     /o:p

} /o:p

 /o:p

Here we can see a java exception being propagated as the message of the SystemException inside the SoapException that we get on the .NET client end./o:p

/o:p 

<?xml:namespace prefix = v ns = “urn:schemas-microsoft-com:vml” />/v:stroke/v:f/v:f/v:f/v:f/v:f/v:f/v:f/v:f/v:f/v:f/v:f/v:f/v:formulas/v:path/o:lock/v:shapetype/o:p

 /o:p

We can specify a message when we create the java.lang.Exception and this is translated across to our .NET client code./o:p

 /o:p

throw new Exception(“something went wrong”);/o:p

 /o:p

  /o:p

 /o:p

This is a good example of how implementation details from the service can “leak out”, so best not to take too much notice of the java.lang.Exception stuff./o:p

 /o:p

Where are we?/o:p

We’ve seen that creating Apache Axis JWS Web Services fairly easily, but that returning anything complex or shaping the WSDL in any way seems hard to impossible. Consuming said services from .NET is extremely straightforward (by creating a proxy class via “add web reference” in VS.NET or WSDL.exe in the framework SDK), and since only simple types can be returned there is a high chance that .NET will be able to interoperate with an Axis JWS Web Service./o:p

 /o:p

Thanks to Java Pete for his help preparing this article.

 

Note: After several edits parts of this post some parts of the XML formatting are getting screwed up. Appologies if some of the fragments are not valid. Also note that BEA has a Web Service technology also called JWS which is quite different to Apache Axis JWS.

/wsdl:types

Comments

john apps
Great article!
Would you post the .NET source, please? Or send it to me at john DOT apps AT hp DOT com

Thank you!
22/03/2006 3:54:00 AM