The best primer in getting started with Web services in .NET, both from a server perspective (where the service is built in .NET and consumed by Java) and from a client perspective (where the service is built in Java and consumed by .NET) is by example. The following sections discuss how to build and consume synchronous Web services using both technologies. The example used is a trivial one, passing a simple data type (string) between the services, but it provides the grounding necessary to understand how to build and deploy both the service and the client. Subsequent chapters look into how to handle much more complicated services and clients. After exploring the Web services technologies provided by Java EE and Microsoft .NET platforms, it is important to talk about how Web services interoperability can be established between the two. This chapter looks into building synchronous Web services communication between Java and .NET applications. WS-I Supply Chain Management Application is used as a sample to implement the interoperability between Java and .NET. Typically there could be two scenarios where integration is important:
There are dos and don'ts for ensuring Web services interoperability between Java EE and Microsoft .NET. It makes the most sense, however, to first focus on establishing simple interoperability between the two platforms using JAX-RPC and then discuss the best practices that will help ensure interoperability. In either of the scenarios just described, there are basic steps that need to be followed to integrate Java EE and Microsoft .NET-based systems using Web services. These are
.NET clients talk to Web services using proxies generated by wsdl.exe as just described. To consume and interoperate with a Java EE-based Web service, the following steps are used:
In the next section these steps are followed to build a Web service on Java EE and consume it using a .NET Framework-based application (see Figure 4-3). This application is based on the Retailer interaction in the WS-I Supply Chain Management use-case. Figure 4-3. Java EE service implementation and .NET clientA Java-Based Web Service with a .NET-Based ClientNow that the basic steps for establishing Java EE .NET interoperability have been introduced, following is a look at a real-world example of how to create interoperable Web services with a Java EE-based service provider and a .NET-based service consumer. Figure 4-4 lists the high-level diagram outlining Retailer services: Figure 4-4. .NET client consuming Java Retailer serviceBuilding the Java Retailer ServiceBuilding this application is very straightforward. The following starts with a simple Java class that implements the Retailer. First is to develop the interface that represents the store. package javaretailer; import java.rmi.RemoteException;import java.rmi.Remote; public interface IRetailer extends Remote { String getProductCatalog () throws RemoteException; } The Java class that implements this interface to create the Retailer service is straightforward: package javaretailer; import java.rmi.RemoteException; import java.rmi.Remote; public class JavaRetailerService implements IRetailer { public String getProductCatalog() throws RemoteException { String strReturn = "We would build a list of items here"; return strReturn; } } Typically these can be compiled using ant, a freely available tool that can be used to script compilation processes. The scripting is achieved using an XML configuration file called build.xml. A snippet of build.xml that compiles this Web service looks like <target name="compile-javaservice" depends=" prepare" > <echo message="--- Compiling application ---"/> <javac srcdir="${src.java}" includes="**/JavaRetailer*.java" destdir="${build.classes}" > <classpath refid = "compile.classpath"/> </javac> </target> ant is invoked and instructed to build the service using the following command: ant compile-javaservice To deploy these as a Web service, the following configuration files are needed. Config.xmlwhich specifies the names of the interface and its implementation. The wscompile tool reads this configuration to generate a WSDL for the Java Web service. It typically looks something like this: <?xml version="1.0" encoding="UTF-8" ?> <configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config"> <!-- The wscompile reads the RMI interface to generate WSDL file. --> <service name="JavaRetailerService" targetNamespace="http://j2eedotnet.com/javaretailer/wsdl" typeNamespace="http://j2eedotnet.com/javaretailer/types" wsdl="http://j2eedotnet.com/javaretailer/WSDL" packageName="javaretailer"> <interface name="javaretailer.IRetailer" servantName="javaretailer.JavaRetailerService"> </service> </configuration> Web.xml is the standard deployment descriptor for Java servers. For the JavaRetailer service, it looks something like this: <?xml version="1.0" encoding=" UTF-8" ?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"> <web-app> <display-name>Java Retailer Web Application</display-name> <description>Java Retailer Web Service</description> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app> Finally, jaxrpc-ri.xml, used by the wsdeploy tool to generate the WAR file that holds the Web service, looks like this: <?xml version="1.0" encoding="UTF-8" ?> <webServices xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd" version="1.0" targetNamespaceBase= "http://j2eedotnet.com/javaretailer/wsdl" typeNamespaceBase= "http://j2eedotnet.com/javaretailer/types" urlPatternBase="/javadotnet"> <!-- The <endpoint> element contains the Web Service's --> <!-- interface and implementation classes --> <endpoint name="IRetailer" displayName="Retailer Service" description="Retailer service" wsdl="/WEB-INF/RetailerService.wsdl" interface="javaretailer.IRetailer " implementation="javaretailer.JavaRetailerService" model="/WEB-INF/model.xml.gz"/> <endpointMapping endpointName="JavaRetailer" urlPattern="/JavaRetailer"/> </webServices> The service can now be built, put into a WAR file, and deployed to an applications server such as Tomcat. Again, this can be achieved using ant. The scripts to build and deploy the application to Tomcat look like this: <!-- Compiling classes --> <target name="compile-javaservice" depends="prepare"> <echo message="--- Compiling application ---"/> <javac srcdir="${src.java}" includes="**/JavaRetailer*.java" destdir="${build.classes}" > <classpath refid = "compile.classpath"/> </javac> </target> <path > <path ref/> <pathelement location="${build.classes}"/> </path> <!-- Generate BookOrder Web Service components --> <target name="generate-server" depends="compile-javaservice"> <wscompile define="true" keep="true" base="${build.classes}" sourceBase="${build.src}" model="${build.model}/model.xml.gz" xPrintStackTrace="true" verbose="false" config="${etc.server}/config.xml"> <classpath ref/> </wscompile> </target> <!-- Archiving BookOrder components into BookOrder-raw.war --> <target name="create-war" depends="generate-server"> <war warfile="${build.home}/JavaRetailer-raw.war" webxml="${etc.server}/web.xml"> <webinf dir="${build.classes}" includes="*.wsdl"/> <webinf dir="${etc.server}" includes="jaxrpc-ri.xml" defaultexcludes="no"/> <webinf dir="${build.model}" includes="model.xml.gz" defaultexcludes="no"/> <classes dir="${build.classes}" includes="**/*.class" defaultexcludes="no"/> </war> </target> <!-- Creating the final WAR file --> <target name="build-war" depends="create-war"> <echo message="--- Building war file ---"/> <wsdeploy keep="true" inWarFile="${build.home}/JavaRetailer-raw.war" outWarFile="${build.home}/JavaRetailer.war" verbose="false" tmpDir="${build.temp}"> <classpath ref/> </wsdeploy> </target> <!-- Deploying the BookOrder Web Service --> <target name="deploy-javaservice" depends="build-war"> <copy file="${build.home}/JavaRetailer.war" todir="${tomcat.root}/webapps"/> </target> When using ant it can either be invoked without a parameter, in which case the entire file will be processed, or it can be passed a specific target such as ant compile-javaservice, in which case the commands specified at <target name="compile-javaservice"> are executed. In the script just listed, the following targets are used:
Once deployed, the application can be run by hitting the context root that is specified in the configuration files. For example it can be run with the following URL if it is deployed to Tomcat (as in this ant script). http://localhost:8080/JavaRetailer To get the WSDL associated with this Web service, ?WSDL is simply applied to the end of the URL, like this: http://localhost:8080/JavaRetailer?WSDL Now that the Java Web Service is established, the next thing to do is to create a client in .NET that consumes this, which is a very straightforward matter. The first thing to do is to generate a .NET proxy to the Web service using the .NET wsdl tool like this wsdl.exe/out:Retailer.cs http://localhost:8080/JavaRetailer?WSDL This generates a proxy module called Retailer.cs, based on the WSDL found at the specified URL. This proxy module can then be used in an application, as follows: using System; public class DotNetClient { public static void Main() { RetailerService srv = new RetailerService(); String result = srv.getProductCatalog(); Console.WriteLine(result); } } This gives a basic, synchronous .NET-based client that consumes a Java-based Web service. A .NET-Based Web Service with a Java-Based ClientIn this section the Retailer Web service is created using .NET technology and a Java client is built that synchronously consumes this service. The high-level diagram for this is shown in Figure 4-5. Figure 4-5. Java client consuming .NET Retailer serviceThe JAX-RPC reference implementation provides a number of tools that simplify building and deploying Java-based Web services. These include the wscompile and wsdeploy tools. These utilities can generate the Java code necessary to link both the client and server implementations to the underlying JAX-RPC infrastructure that ultimately creates or consumes SOAP messages. Application server vendors also provide similar tools. Using the wscompile tool, it is possible to generate the client side stubs for a .NET-based Web service. These stubs can be used to write a JAX-RPC client for the Web service. The Java EE model for Web service consumption in Figure 4-6 requires the following steps: Figure 4-6. .NET service implementation and Java EE client
The easiest way to develop a Web service with .NET is to use the Visual Studio.NET tool from Microsoft. From here, File>New>Web Service is selected to generate a basic skeleton Web service. One called DotNetRetailer.asmx can be created. Then underlying C# code for this Web service should then be changed to the following: using System; using System.Web.Services; namespace dotnetretailer { public class DotNetRetailService : System.Web.Services.WebService { [WebMethod(Description = "BookOrder Web Service")] public string getProductCatalog() { string strReturn = "The Product Catalog would go here."; return strReturn; } } } This can then be compiled and deployed to IIS using the IDE, and the Web service can be accessed with this URL: http://localhost/DotNetRetailer/DotNetRetailer.asmx As mentioned earlier, the WSDL for the service is accessible using ?WSDL at the end of the URL, like this http://localhost/DotNetRetailer/DotNetRetailer.asmx?WSDL To create a client to this Web service in Java, the wscompile tool can be used. This requires a configuration file containing the settings that drive it to create Java stubs for a proxy to this service. The configuration file will look like this <configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config" > <wsdl location="http://localhost/DotNetRetailer/DotNetRetailer.asmx?WSDL" packageName="dotnetretailer"/> </configuration> Point wsconfig at this configuration file and it will generate the stubs for talking to this Web service in the package dotnetretailer. These stubs can then be used to talk to the .NET Web service in a Java client like this package dotnetretailer; import javax.xml.rpc.Stub; public class JavaClient { public static void main (String[] args) { try { DotNetRetailService_Stub stub = (DotNetRetailServiceSoap_Stub) (new DotNetRetailService_Impl().getDotNetRetailService Soap()); stub._setProperty( javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, System.getProperty("endpoint")); System.out.println(stub.getProductCatalog()); } catch (Exception ex) { ex.printStackTrace(); } } } This client will now consume the .NET-based Retailer service. |