Vous êtes sur la page 1sur 38

Web Services and the .

NET Framework
Building a Web Service Client using C# and .NET Gopalan Suresh Raj Note
To work with any of these samples, you will need the following:
.........................................Microsoft .NET SDK .........................................Microsoft Visual Studio.NET Beta 2 or higher

Building Web Service Clients using the .NET Framework


Clients communicate with Web Services using standard web protocols, using XML-encoded messages to send and receive messages between themselves. This makes it possible for client applications running on any platform to access web services as long as they use standard web protocols, and understand the XML-encoded messages. There are three protocols that web clients can use to communicate with web services namely, HTTP GET, HTTP POST, and SOAP. The various steps that are involved in creating a Web Service Client using C# and the .NET Framework are as follows: 1. Generate a Proxy for the Web Service 2. Compile the Proxy as a DLL library 3. Create a Visual C# - Console Application Project

4. Develop the Client.cs class file


5. Build the Project Files

1. Generate a Proxy class for the Web Service


The .NET SDK simplifies the process of creating Web Service clients by providing the Web Services Description Language (wsdl.exe) utility. This utility generates proxy source code for an existing Web Service, just as IDL compilers generate DCOM proxies for DCOM components. The only difference between IDL and WSDL is that, WSDL is a language that describes the interface of a software component that is XML-based. Let us generate source code for the proxies to the actual web service as shown below: Command Prompt
C:\MyProjects\Cornucopia\WebService\Client>wsdl /l:CS /protocol:SOAP http://localhost/OIDServer/OIDServer.asmx?WSDL Microsoft (R) Web Services Description Language Utility [Microsoft (R) .NET Framework, Version 1.0.2914.16] Copyright (C) Microsoft Corp. 1998-2001. All rights reserved. Writing file 'C:\MyProjects\Cornucopia\WebService\Client\OIDServer.cs'. C:\MyProjects\Cornucopia\WebService\Client>

The above command creates a proxy for the OIDServer web service from the WSDL document obtained from the URL http://localhost/OIDServer/OIDServer.asmx?WSDL. The proxy uses SOAP as its protocol to talk to the web service and is generated as a C# source file which is shown below for your perusal.

OIDServer.cs
1: 2: 3: 4: 5: 6: 7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : 21 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 31 : //-----------------------------------------------------------------------------// <autogenerated> // This code was generated by a tool. // Runtime Version: 1.0.2914.16 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </autogenerated> //-----------------------------------------------------------------------------// // This source code was auto-generated by wsdl, Version=1.0.2914.16. // using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.Web.Services; [System.Web.Services.WebServiceBindingAttribute(Name="OIDServerSoap", Namespace="http://icommware.com")] public class OIDServer : System.Web.Services.Protocols.SoapHttpClientProtocol { [System.Diagnostics.DebuggerStepThroughAttribute()] public OIDServer() { this.Url = "http://localhost/OIDServer/OIDServer.asmx"; } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://icommware.com/ge nerateOID", RequestNamespace="http://icommware.com", ResponseNamespace="http://icommware.com", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public string generateOID() { object[] results = this.Invoke("generateOID", new object[0]); return ((string)(results[0])); } [System.Diagnostics.DebuggerStepThroughAttribute()] public System.IAsyncResult BegingenerateOID(System.AsyncCallback callback, object asyncState) { return this.BeginInvoke("generateOID", new object[0], callback, asyncState); } [System.Diagnostics.DebuggerStepThroughAttribute()] public string EndgenerateOID(System.IAsyncResult asyncResult) {

32 object[] results = this.EndInvoke(asyncResult); : return ((string)(results[0])); 33 } : } 34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 47 : 48 : 49 : 50 : 51 : The wsdl.exe utility can also take a WSDL file as input instead of a URL pointing to the location where the WSDL can be obtained. This C# proxy source file represents the proxy class for the OIDServer web service that clients can compile against. If you examine the above class, you will notice that it contains an OIDServer proxy class that derives from the System.Web.Services.Protocols.SoapHttpClientProtocol class. If you use the /protocol:HttpGet or /protocol:HttpPost parameters, the OIDServer derives from either the System.Web.Services.Protocols.HttpGetClientProtocol class or the System.Web.Services.Protocols.HttpPostClientProtocol class.

2. Compile the Proxy class as a DLL Library

We can compile the C# source file into a dynamic link library (DLL) and then add a reference to this DLL to any project you want to create. Compile the proxy class as a DLL as shown below: Command Prompt
C:\MyProjects\Cornucopia\WebService\Client>csc /t:library /r:System.Web.Services.dll /r:System.Xml.dll OIDServer.cs Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR version v1.0.2914] Copyright (C) Microsoft Corp 2000-2001. All rights reserved. C:\MyProjects\Cornucopia\WebService\Client>

3. Create a Visual C# - ASP.NET Web Service project


Create a new Visual C# ASP.NET Web Service project. Create a Reference to the OIDServer.dll library.

4. Develop the Client.cs class file


On Line 40, we create an instance of the proxy to the web service OIDServer. Line 43 invokes the generateOID() web method to get a string as the result.

Client.cs
1: 2: 3: 4: 5: 6: ////////////////////////////////////////////////////// /// The following example illustrates a Client to a /// WebService developed using C# and the .NET Framework. /// /// author: Gopalan Suresh Raj /// Copyright (c), 2002. All Rights Reserved.

7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : 21 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 31 : 32 : 33 : 34 : 35 :

/// URL: http://gsraj.tripod.com/ /// email: gopalan@gmx.net /// /// <generate> /// wsdl /l:CS /protocol:SOAP http://localhost/OIDServer/OIDServer.asmx?WSDL /// wsdl /l:CS /protocol:HttpGet http://localhost/OIDServer/OIDServer.asmx?WSDL /// wsdl /l:CS /protocol:HttpPost http://localhost/OIDServer/OIDServer.asmx?WSDL /// </generate> /// <compile> /// csc /t:library /r:System.Web.Services.dll /r:System.Xml.dll OIDServer.cs /// </compile> /// <compile> /// csc Client.cs /r:OIDServer.dll /// </compile> ////////////////////////////////////////////////////// using System; namespace TestOIDServer { /// <summary> /// Summary description for Client. /// </summary> public class Client { /// <summary> /// Default No argument constructor /// </summary> public Client() { } /// <summary> /// Entry Point to this Application /// </summary> public static void Main () { // Create a proxy OIDServer server = new OIDServer(); // Invoke generateOID() over SOAP and get the new OID string oid = server.generateOID (); // Print out the value Console.WriteLine ("The new OID is :"+oid); } } }

36 : 37 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 47 : 48 : 49 :

5. Build the Project Files


Build the files that make up the project.
------ Rebuild All started: Project: Client, Configuration: Debug .NET -----Preparing resources... Updating references... Performing main compilation... Build complete -- 0 errors, 0 warnings Building satellite assemblies...

---------------------- Done ---------------------Rebuild All: 1 succeeded, 0 failed, 0 skipped

6. Run the Client


Command Prompt
C:\MyProjects\Cornucopia\WebService\Client>Client The new OID is :16995955-e0ca-43b3-8279-edc14ca8dbca C:\MyProjects\Cornucopia\WebService\Client>

Web Services and the .NET Framework


Building a Web Service component using C# and .NET Gopalan Suresh Raj Note
To work with any of these samples, you will need the following:
.........................................Microsoft .NET SDK .........................................Microsoft Visual Studio.NET Beta 2 or higher To ease development, I recommend using the Visual Studio.NET IDE. However, you are free to develop your application in the favorite editor of your choice, using the command-line to execute the various commands to build and deploy it.

Building Web Service components using the .NET Framework


Web Services run on top of HTTP. Therefore, they are developed and deployed on a Web Server and their URL is advertised so that "Internet" clients can discover and make use of their services. Even though that Web Server can be Microsoft IIS, or Apache, or any other such server, at this point in time, the .NET framework only supports deployment on Microsoft IIS. In this article, we will build a web service called OIDServer that clients can use to generate a unique Object Identity (OID) string. The generated OID string is unique, and can be used by the client wherever unique keys are needed - either for COM GUIDs, or to act as a unique primary key for a database table, etc. Since the Web Service class is deployed on a Web Server, any "Internet" client can access it and make use of its services - as opposed to COM components which can only be accessed by COM clients. The various steps that are involved in creating a Web Service Component using C# and the .NET Framework are as follows (I'm going to assume you're using the VS.NET IDE): 1. Create a Visual C# - ASP.NET Web Service project

2. Develop the OIDServer.asmx file and the OIDServer.asmx.cs class file 3. Modify the generated AssemblyInfo.cs to add the right assembly information
4. Build the Project Files

1. Create a Visual C# - ASP.NET Web Service project


Create a new Visual C# ASP.NET Web Service project.

2. Develop the OIDServer.asmx file and the OIDServer.asmx.cs class file


Visual Studio.NET creates a bunch of files for you to use while developing your web service. Of particular interest for our discussion now are, the OIDServer.asmx file and the OIDServer.asmx.cs class file. The OIDServer.asmx file OIDServer.asmx contains the <%@ WebService... %> directive, as well as the class that provides the implementation in the Class="OIDServer.OIDServer" directive. To any web service client, the .asmx file serves as the main entry point to the Web Service. This file needs to be hosted on a virtual directory on IIS that has the executescripts permission turned on.

OIDServer.asmx
1 <%@ WebService Language="c#" Codebehind="OIDServer.asmx.cs" : Class="OIDServer.OIDServer" %> People familiar with ASP will immediately recognize the @ symbol in front of the WebService keyword. The WebService directive specifies the language used to develop the web service so that ASP.NET can compile this service with the right compiler. The Class="OIDServer.OIDServer" directive specifies the class that implements the web service, so that the right class can be loaded and its Web Services Description (WSDL) generated using reflection. The OIDServer.asmx.cs Class file

As shown on Line 34, Web Services derive from the System.Web.Services.WebService class which allows the web service object to access all normal ASP objects exposed in the WebService base class. This means, the OIDServer class can use ASP objects as though it were a regular ASP based application. As the default namespace http://tempuri.org/ will not uniquely identify our web service from other web services on the internet, we specify our own namespace http://icommware.com before publishing our web service publicly as shown on Line 32. The Namespace = "http://icommware.com" attribute facilitates us to specify our own namespace. Unless you don't want to publish a method, you need to tag all public methods with the WebMethod attribute to make web methods public methods of a distributed component that are accessible though the world-wide web. This is shown on line 64.

OIDServer.asmx.cs
1: 2: 3: 4: 5: 6: 7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : 21 : 22 : 23 : 24 : 25 : 26 ////////////////////////////////////////////////////// /// The following example illustrates a Web Service /// developed using C# and the .NET Framework. /// /// author: Gopalan Suresh Raj /// Copyright (c), 2002. All Rights Reserved. /// URL: http://gsraj.tripod.com/ /// email: gopalan@gmx.net /// /// <generate> /// wsdl /l:CS /protocol:SOAP http://localhost/OIDServer/OIDServer.asmx?WSDL /// wsdl /l:CS /protocol:HttpGet http://localhost/OIDServer/OIDServer.asmx?WSDL /// wsdl /l:CS /protocol:HttpPost http://localhost/OIDServer/OIDServer.asmx?WSDL /// </generate> /// <compile> /// csc /t:library /r:System.Web.Services.dll /r:System.Xml.dll OIDServer.dll /// </compile> ////////////////////////////////////////////////////// using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; using System.EnterpriseServices; namespace OIDServer { /// <summary> /// Summary description for OIDServer. /// </summary> [WebService ( Namespace = "http://icommware.com", Name = "OIDServer", Description = "Generates unique Object Identity (OID)" )] public class OIDServer : System.Web.Services.WebService { /// <summary> /// Default No argument constructor /// </summary> public OIDServer() { //CODEGEN: This call is required by the ASP.NET Web Services Designer InitializeComponent(); }

: 27 #region Component Designer generated code : /// <summary> 28 /// Required method for Designer support - do not modify : /// the contents of this method with the code editor. 29 /// </summary> : private void InitializeComponent() { 30 } : #endregion 31 : /// <summary> 32 /// Clean up any resources being used. : /// </summary> 33 protected override void Dispose( bool disposing ) { : } 34 : /// <summary> 35 /// Generates unique Object Identity (OID). Returns a String representation : /// of the value of a new GUID in the Windows Registry format. 36 /// </summary> : /// <returns></returns> 37 [WebMethod( EnableSession = true, TransactionOption = TransactionOption.Disabled, : BufferResponse = false, CacheDuration = 0, MessageName = 38 "generateOID", : Description = "Generates unique Object Identity (OID)" )] 39 public string generateOID () { : return Guid.NewGuid ().ToString(); 40 } : } 41 } : 42 : 43 : 44 : 45 : 46 : 47 : 48 : 49 : 50 : 51 : 52 : 53 :

54 : 55 : 56 : 57 : 58 : 59 : 60 : 61 : 62 : 63 : 64 : 65 : 66 : 67 : 68 : 69 : 70 : 71 : Notice that the generateOID() method on Line 64 is tagged with the WebMethod tag using the [] syntax. By specifying the [WebMethod] tag in front of this public method, we make this public method callable from any Internet client. Behind the scenes, the generateOID() method is associated with an attribute which is implemented as a WebMethodAttribute class. This class has six properties which are also specified from Lines 64 through 66. The EnableSession property enables or disables session state. If you don't intend to use session state for this web method, you should disable this flag so that the web server does not generate and manage session IDs for every user accessing this web method. This might improve performance. This flag is true by default. The TransactionOption property can be one of five values: Disabled, NotSupported, Supported, Required, and RequiresNew. As Web Methods can participate only as the root object in a Transaction, both Required and RequiresNew options result in a new transaction being created for the Web Method. The Disabled, NotSupported, and Supported options result in no transactions being used for the web methods. The TransactionOption property is set to Disabled by default.

The BufferResponse property controls whether or not to buffer the method's response. The CacheDuration property specifies the length of time, in seconds, to keep the method response in the cache. The default does not hold the response in the cache (0 seconds). The MessageName property is used to distinguish web methods with the same names. For example, if you have two methods with the same name, and you want to publish both these methods as web methods, the system will have a problem identifying one from the other as their names are duplicated. This property ensures that all service signatures in the WSDL are unique. The Description property provides additional descriptive information about a particular method.

3. Modify the generated AssemblyInfo.cs to add the right assembly

information
You provide the compiler with your assembly information in an assembly file called AssemblyInfo.cs. The assembly information file is compiled with the rest of the project's source files. The information is in the form of assembly attributes - directives to the compiler on the information to embed in the assembly.

AssemblyInfo.cs
1: 2: 3: 4: 5: 6: 7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : 21 using System.Reflection; using System.Runtime.CompilerServices; // // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. // [assembly: AssemblyTitle("OID Server")] [assembly: AssemblyDescription("Creates unique Object Identity (OID)")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("eCommWare Corporation")] [assembly: AssemblyProduct("OID Server")] [assembly: AssemblyCopyright("(c) 2001, Gopalan Suresh Raj. All Rights Reserved.")] [assembly: AssemblyTrademark("Web Cornucopia")] [assembly: AssemblyCulture("en-US")] // // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.*")] // // In order to sign your assembly you must specify a key to use. Refer to the

: 22 : 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 31 : 32 : 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 47 : 48 :

// Microsoft .NET Framework documentation for more information on assembly signing. // // Use the attributes below to control which key is used for signing. // // Notes: // (*) If no key is specified, the assembly is not signed. // (*) KeyName refers to a key that has been installed in the Crypto Service // Provider (CSP) on your machine. KeyFile refers to a file which contains // a key. // (*) If the KeyFile and the KeyName values are both specified, the // following processing occurs: // (1) If the KeyName can be found in the CSP, that key is used. // (2) If the KeyName does not exist and the KeyFile does exist, the key // in the KeyFile is installed into the CSP and used. // (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. // When specifying the KeyFile, the location of the KeyFile should be // relative to the "project output directory". The location of the project output // directory is dependent on whether you are working with a local or web project. // For local projects, the project output directory is defined as // <Project Directory>\obj\<Configuration>. For example, if your KeyFile is // located in the project directory, you would specify the AssemblyKeyFile // attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] // For web projects, the project output directory is defined as // %HOMEPATH%\VSWebCache\<Machine Name>\<Project Directory>\obj\<Configuration>. // (*) Delay Signing is an advanced option - see the Microsoft .NET Framework // documentation for more information on this. // [assembly: AssemblyDelaySign(false)] [assembly: AssemblyKeyFile("")] [assembly: AssemblyKeyName("")]

49 : 50 : 51 : 52 : 53 : 54 : 55 : 56 : 57 : 58 : 59 : 60 : 61 : 62 : The OIDServer.vsdisco file Whenever a remote (or local) client is interested in using a Web Service, the first step is to determine which web services exist on a given machine. Even though the .NET class library provides the API that allows you to examine registered Web Services programmatically, discovery services are also required by numerous case tools. The .vsdisco file is used to describe each Web Service in a given virtual directory and any related subfolders.

OIDServer.vsdisco
1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 <?xml version="1.0" ?> <dynamicDiscovery xmlns="urn:schemas-dynamicdiscovery:disco.2000-03-17"> <exclude path="_vti_cnf" /> <exclude path="_vti_pvt" /> <exclude path="_vti_log" /> <exclude path="_vti_script" /> <exclude path="_vti_txt" /> <exclude path="Web References" /> </dynamicDiscovery>

: 9 : The Web.config file The generated Web.config file should normally be placed in the same directory as the .asmx file. This configuration file allows you to control various application settings of the virtual directory. To ease development and testing, it is recommended that you set the authentication mode to None as shown on Line 28. However, when releasing the web service to the public, you can change it to either Windows, or Forms, or Passport, as opposed to None. The authentication mode set to Windows indicates that authentication is performed by IIS in one of three ways - basic, digest, or Integrated Windows Authentication. The authentication mode set to Forms indicates that un-authenticated requests are redirected to a login page. The authentication mode set to Passport indicates that un-authentication requests are directed to Microsoft's centralized authentication service. When authenticated, a token is passed back and used by subsequent requests.

Web.config
1: 2: 3: 4: 5: 6: 7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <!-- DYNAMIC DEBUG COMPILATION Set compilation debug="true" to enable ASPX debugging. Otherwise, setting this value to false will improve runtime performance of this application. Set compilation debug="true" to insert debugging symbols (.pdb information) into the compiled page. Because this creates a larger file that executes more slowly, you should set this value to true only when debugging and to false at all other times. For more information, refer to the documentation about debugging ASP.NET files. --> <compilation defaultLanguage="c#" debug="true" /> <!-- CUSTOM ERROR MESSAGES Set mode="on" or "remoteonly" to enable custom error messages, "off" to disable. Add <error> tags for each of the errors you want to handle. --> <customErrors mode="Off" /> <!-- AUTHENTICATION This section sets the authentication policies of the application. Possible modes are "Windows", "Forms", "Passport" and "None" --> <authentication mode="None" />

: 21 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 31 : 32 : 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 47 :

<!-- APPLICATION-LEVEL TRACE LOGGING Application-level tracing enables trace log output for every page within an application. Set trace enabled="true" to enable application trace logging. If pageOutput="true", the trace information will be displayed at the bottom of each page. Otherwise, you can view the application trace log by browsing the "trace.axd" page from your web application root. --> <trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" /> <!-- SESSION STATE SETTINGS By default ASP.NET uses cookies to identify which requests belong to a particular session. If cookies are not available, a session can be tracked by adding a session identifier to the URL. To disable cookies, set sessionState cookieless="true". --> <sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;user id=sa;password=" cookieless="false" timeout="20" /> <!-- PREVENT SOURCE CODE DOWNLOAD This section sets the types of files that will not be downloaded. As well as entering a httphandler for a file type, you must also associate that file type with the aspnet_isapi.dll in the App Mappings property of the web site, or the file can be downloaded. It is recommended that you use this section to prevent your sources being downloaded. --> <httpHandlers> <add verb="*" path="*.vb" type="System.Web.HttpNotFoundHandler,System.Web" /> <add verb="*" path="*.cs" type="System.Web.HttpNotFoundHandler,System.Web" /> <add verb="*" path="*.vbproj" type="System.Web.HttpNotFoundHandler,System.Web" /> <add verb="*" path="*.csproj" type="System.Web.HttpNotFoundHandler,System.Web" /> <add verb="*" path="*.webinfo" type="System.Web.HttpNotFoundHandler,System.Web" /> </httpHandlers> <!-- GLOBALIZATION This section sets the globalization settings of the application. --> <globalization

48 requestEncoding="utf-8" : responseEncoding="utf-8" 49 /> : </system.web> 50 </configuration> : 51 : 52 : 53 : 54 : 55 : 56 : 57 : 58 : 59 : 60 : 61 : 62 : 63 : 64 : 65 : 66 : 67 : 68 : 69 : 70 : 71 : 72 : 73 : 74 : 75

: 76 :

4. Build the Project Files


Build the files that make up the project.
------ Rebuild All started: Project: OIDServer, Configuration: Debug .NET -----Preparing resources... Updating references... Performing main compilation... Build complete -- 0 errors, 0 warnings Building satellite assemblies...

---------------------- Done ---------------------Rebuild All: 1 succeeded, 0 failed, 0 skipped

5. Deploy the Web Service files on IIS, and test your Web Service
As soon as you compile your Web Service, you can use it by executing it from the Visual Studio.NET IDE - Run or Debug the Application. By default, the machine's active browser functions as a makeshift client, that shows a HTML view of all the methods marked with the [WebMethod] attribute as shown in the figure below.

You can invoke the generateOID() method by clicking the generateOID link and you'll get the following page shown.

When you click on the Invoke button, the web service is invoked and the result is displayed on the next page.

Web Services and the .NET Framework


Accessing a .NET Web Service using Apache/SOAP and Java Gopalan Suresh Raj Note
To work with any of these samples, you will need the following:
.........................................Microsoft .NET SDK or .........................................Microsoft Visual Studio.NET Beta 2 or higher .........................................Java JDK ver 1.3 or higher .........................................Java Mail ver 1.2 or higher .........................................Java Activation Framework .........................................Apache SOAP ver 2.2 or higher .........................................Apache Xerces (a Java XML Parser)

Building .NET Web Service Clients using Apache/SOAP and Java


In this example, we are going to build an Apache/SOAP Java Client to the OIDServer Web Service that we created earlier using C#/.NET. Since the Apache SOAP Toolkit is designed as a Remote Procedure Call (RPC) mechanism as opposed to a document exchange mechanism, it expects RPC encoding. Therefore, the various steps that are involved in creating a .NET Web Service Client using Apache/SOAP and Java are as follows:

1. Develop a Class that generates the SOAP message Body element - the
ApacheMessageBody.java class file

2. Develop a Class that parses the SOAP response - the SAXHandler.java class file 3. Develop a Java Proxy for the .NET endpoint - the ApacheSoapProxy.java class file 4. Develop the Actual Client - Client.java class file
5. Build and Run

1. Develop a Class that generates the SOAP message Body element

Apache SOAP performs serialization and deserialization using a class called Body, which uses its marshall() and unmarshall() methods to perform the respective operations. While marshall() is an instance method, unmarshall() is a static class method. This means, it is not possible to just inherit from the Body class and override the unmarshall() method. However, you can change the Serialization behavior of the Body class by overriding the marshall() method as shown from Lines 30 through 51 in the code below. Depending on the number of methods that you may have, you can either create multiple Body implementations, each with their own marshall() methods, or you may choose to build one Body implementation with a single marshall()method which can choose the right code to execute based on other information. Since ASP.NET endpoints use document/literal encoding as opposed to RPC encoding, you only need to write out the following information: Write out the Body Element (as shown on Lines 36-39 ) Write out the method name and namespace information (as shown on Line 42 ) Write out the arguments that are passed into the method (as shown on Lines 43 )

Notice that in the code below, we extend the Body class as shown on line 20

ApacheMessageBody.java
1: 2: 3: 4: 5: 6: 7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : 21 : ////////////////////////////////////////////////////// /// This Class generates the SOAP message Body element /// /// author: Gopalan Suresh Raj /// Copyright (c), 2002. All Rights Reserved. /// URL: http://gsraj.tripod.com/ /// email: gopalan@gmx.net ////////////////////////////////////////////////////// import import import import import java.io.*; org.apache.soap.*; org.apache.soap.util.*; org.apache.soap.util.xml.*; org.apache.soap.rpc.SOAPContext;

/** * This Class generates the SOAP message Body element * @author Gopalan Suresh Raj */ public class ApacheMessageBody extends Body { /** potential argument to the web method. */ //public String inputString_; /** * Override the Apache default marshall method * and change how the SOAP Body element * is serialized. */ public void marshall (String inScopeEncodingStyle, Writer sink, NSStack nameSpaceStack, XMLJavaMappingRegistry registry,

22 SOAPContext context) throws IllegalArgumentException, IOException : { 23 // Set the Body element : String soapEnvironmentNamespacePrefix = "SOAP-ENV"; 24 sink.write ('<'+soapEnvironmentNamespacePrefix+':'+ : Constants.ELEM_BODY+'>'+ 25 StringUtils.lineSeparator); : 26 // Write out the method name and related argument (s) : sink.write ("<generateOID xmlns=\""http://tempuri.org/\">"+ 27 //"<inputString>"+inputString_+"</inputString>"+ : "</generateOID>"); 28 : // Close the Body element 29 sink.write ("</" + soapEnvironmentNamespacePrefix+':'+ : Constants.ELEM_BODY+'>'+ 30 StringUtils.lineSeparator); : nameSpaceStack.popScope (); 31 } : } 32 : 33 : 34 : 35 : 36 : 37 : 38 : 39 : 40 : 41 : 42 : 43 : 44 : 45 : 46 : 47 : 48 : 49

: 50 : 51 : 52 : Now that you can send the message, you should be able to read the message. To read the message, you need a class that can handle SAX Processing for you and read the data.

2. Develop a Class that parses the SOAP Response


Whenever a message goes out and comes back, the entire SOAP response can be determined. The SAXHandler class will be used by the Proxy that we will build soon. The SAX Response Handler that we're building now is a general purpose class that can obtain any single element response. Therefore, users should be able to use the class without any other modifications. If the value is a Boolean, or other numeric, or date types, you can perform the conversion after you retrieve the result using the getResult() method to obtain the single element response. You will be required to modify the code for complex types and arrays.

SAXHandler.java
1: 2: 3: 4: 5: 6: 7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 ////////////////////////////////////////////////////// /// This Class parses the SOAP response /// /// author: Gopalan Suresh Raj /// Copyright (c), 2002. All Rights Reserved. /// URL: http://gsraj.tripod.com/ /// email: gopalan@gmx.net ////////////////////////////////////////////////////// import org.xml.sax.*; import org.xml.sax.helpers.*; /** * This Class parses the SOAP response. This is a * general purpose class that can obtain any single * element response. * * @author Gopalan Suresh Raj */ public class SAXHandler extends DefaultHandler { private String indent_ = ""; private String result_ = ""; private String elementToSearchFor_ = ""; private boolean foundResult_ = false; /** Default No argument constructor */ public SAXHandler () { }

: /** 21 * Retrieve the result for a single element : * The code has to be modified as necessary 22 * for more complex types and arrays : */ 23 public String getResult () { : return result_; 24 } : 25 /** Provide the element name to search for */ : public void setElementToSearchFor (String elementName) { 26 elementToSearchFor_ = elementName; : } 27 : /** Retrieve the set element name */ 28 public String getElementToSearchFor () { : return elementToSearchFor_; 29 } : 30 /** : * Overriden method of the DefaultHandler class to 31 * gain notification of all SAX events : */ 32 public void startElement (String namespaceURI, : String localName, 33 String qName, : Attributes attributes) throws SAXException { 34 if (foundResult_ == false) { : foundResult_ = (localName.compareTo (elementToSearchFor_) == 0); 35 } : } 36 : /** 37 * Overriden method of the DefaultHandler class to : * gain notification of all SAX events 38 */ : public void characters (char[] characters, int start, int length) throws SAXException { 39 if (foundResult_ == true) { : result_ = String.valueOf (characters, start, length); 40 foundResult_ = false; : } 41 } : 42 } : 43 : 44 : 45 : 46 : 47 :

48 : 49 : 50 : 51 : 52 : 53 : 54 : 55 : 56 : 57 : 58 : 59 : 60 : 61 : 62 : 63 : 64 : 65 : 66 : 67 : 68 : 69 : 70 : 71 : 72 : 73 : 74 :

3. Develop a Java Proxy for the .NET Endpoint


Even though the IBM Web Services toolkit can produce proxies, when it comes to .NET interoperability, it is easier to build one by hand. The proxy will facilitate Client Applications to instantiate the proxy and invoke method calls on the proxy, all the while only worrying about handling SOAP faults. The proxy shown below performs the following sets of operations: Verifies that the URL set is non-null Constructs a Message Sends over the Message Parses the Response.

Since Apache SOAP 2.2 classes encode messages using RPC encoding, for .NET interoperability which uses document/literal encoding, you must override the code that constructs the body in addition to the code that interprets the response.

ApacheSoapProxy.java
1: 2: 3: 4: 5: 6: 7: 8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : 21 : 22 ////////////////////////////////////////////////////// /// This Class creates a Java Proxy for the .NET endpoint /// /// author: Gopalan Suresh Raj /// Copyright (c), 2002. All Rights Reserved. /// URL: http://gsraj.tripod.com/ /// email: gopalan@gmx.net ////////////////////////////////////////////////////// import import import import import import import import java.io.*; java.net.*; javax.activation.*; org.apache.soap.*; org.apache.soap.messaging.*; org.xml.sax.*; org.xml.sax.helpers.*; org.apache.xerces.parsers.SAXParser;

/** * This Class creates a Java Proxy for the .NET endpoint * @author Gopalan Suresh Raj */ public class ApacheSoapProxy { private URL url_ = null; private String soapActionUri_ = ""; private Message message_ = new Message (); private Envelope envelope_ = new Envelope (); DataHandler soapMessage_ = null; /** Default No argument constructor */ public ApacheSoapProxy () throws MalformedURLException { this.url_ = new URL ("http://localhost/OIDServer/OIDServer.asmx"); }

: /** Set the End Point URL */ 23 public synchronized void setEndPoint (URL url) { : this.url_ = url; 24 } : 25 /** Retrieve the End Point URL */ : public synchronized URL getEndPoint () { 26 return this.url_; : } 27 : /** 28 * Apache 2.2 classes encode messages differently than .NET does. : * Therefore we have to override the piece that builds the body and 29 * the pieces that interpret the response. : */ 30 public synchronized String generateOID () throws SOAPException { : String returnValue = ""; 31 : if (this.url_ == null) { 32 throw new SOAPException (Constants.FAULT_CODE_CLIENT, : "A URL must be specified through "+ 33 "ApacheSoapProxy.setEndPoint(URL)"); : } 34 // Get this from the soapAction attribute on the : // soap:operation element that is found within the SOAP 35 // binding information in the WSDL : this.soapActionUri_ = "http://icommware.com/generateOID"; 36 ApacheMessageBody ourBody = new ApacheMessageBody (); : 37 // Set the argument : //theBody.inputString_ = ""; 38 : // Replace the default body with our own 39 this.envelope_.setBody (ourBody); : message_.send (this.getEndPoint(), this.soapActionUri_, this.envelope_); 40 : try { 41 // Since the Body.unmarshall() handler is static, we can't : // replace the basic machinery easily. Instead, we must obtain 42 // and parse the message on our own. : this.soapMessage_ = this.message_.receive(); 43 XMLReader reader = : (XMLReader)Class.forName("org.apache.xerces.parsers.SAXParser").newInstance(); 44 SAXHandler handler = new SAXHandler(); : handler.setElementToSearchFor ("generateOIDResult"); 45 : // Set the Content Handler 46 reader.setContentHandler (handler); : 47 // Parse the file : reader.parse ( new InputSource (new StringReader 48 (this.soapMessage_.getContent().toString() ))); : 49 // If we reached here, the result has been parsed and : // stored in the handler instance.

50 returnValue = handler.getResult (); : } 51 catch (Exception exception) { : exception.printStackTrace (); 52 } : return returnValue; 53 } : 54 } : 55 : 56 : 57 : 58 : 59 : 60 : 61 : 62 : 63 : 64 : 65 : 66 : 67 : 68 : 69 : 70 : 71 : 72 : 73 : 74 : 75 : 76 : 77

: 78 : 79 : 80 : 81 : 82 : 83 : 84 : 85 : 86 : 87 : 88 : 89 : 90 : 91 : 92 : 93 : 94 : 95 : 96 :

4. Develop the Actual Client


Notice that on Line 23, we instantiate the Proxy, and on line 26, we call the web method on the proxy.

Client.java
1: 2: 3: 4: 5: 6: 7: ////////////////////////////////////////////////////// /// The following example illustrates a Client to a /// WebService developed using C# and the .NET Framework. /// /// author: Gopalan Suresh Raj /// Copyright (c), 2002. All Rights Reserved. /// URL: http://gsraj.tripod.com/

8: 9: 10 : 11 : 12 : 13 : 14 : 15 : 16 : 17 : 18 : 19 : 20 : 21 : 22 : 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 31 : 32 : 33 : 34 : 35 : 36

/// email: gopalan@gmx.net ////////////////////////////////////////////////////// import java.io.*; /** * Web Service Client Class * @author Gopalan Suresh Raj */ public class Client { /** Entry Point to this Application */ public static void main(String[] args) { try { // Create a proxy ApacheSoapProxy proxy = new ApacheSoapProxy (); // Invoke generateOID() over SOAP and get the new OID String result = proxy.generateOID (); // Print out the value System.out.println ("The new OID is :"+result); } catch (java.net.MalformedURLException exception) { exception.printStackTrace (); } catch (org.apache.soap.SOAPException exception) { exception.printStackTrace (); } } }

: 37 : 38 :

5. Compile the classes


Compile the classes as shown below: Command Prompt
C:\MyProjects\Cornucopia\WebService\javaSoapClient>javac -classpath .;C:\java\soap2_2\lib\soap.jar;C:\java\jaf-1.0.1\activation.jar;C:\java\javamail-1.2\mail.jar;C:\java\xerces1_4_3\xerces.jar ApacheSoapProxy.java Client.java SAXHandler.java ApacheMessageBody.java C:\MyProjects\Cornucopia\WebService\javaSoapClient>

6. Run the Client


Command Prompt
C:\MyProjects\Cornucopia\WebService\javaSoapClient>java -cp .;C:\java\xerces1_4_3\xerces.jar;C:\java\soap-2_2\lib\soap.jar;C:\java\jaf-1.0.1\activation. jar;C:\java\javamail-1.2\mail.jar; Client The new OID is :0d70d55a-b57e-4402-9e2e-1c51f20469b3 C:\MyProjects\Cornucopia\WebService\javaSoapClient>

Integrating J2EE and .NET Web Services


Integrating J2EE and .NET Web Services
BY EVELYN HOBSON ARTICLE RATING:

Top of Form

node 42838 /fivestar/vote/nod Select rating


GIVE GIVE GIVE GIVE GIVE IT IT IT IT IT 1/5 2/5 3/5 4/5 5/5

node/42838

Rate form-f0f438d29b

fivestar_form_no
Bottom of Form
MARCH 4, 2003 12:00 AM EST READS:

10,295

Related Print Email Feedback Add This Blog This

Many articles have claimed that Java 2, Enterprise Edition (J2EE) and .NET Web services are interoperable. What does this mean from a developer's perspective? What issues and problems arise as you actually do the work? For our purposes, I'll define Web services as self-contained, modular business applications that have open, Internet-oriented, standards-based interfaces. The standards I'm referring to are Simple Object Access Protocol (SOAP) and Web Services Definition Language (WSDL). SOAP is a lightweight XMLbased message format for communication between Web services. WSDL is a schema that describes the way in which a Web service is to be used. WSDL documents answer the following questions about a Web service: What data is exchanged, how do you interact with the service, and where is the service located? When I refer to term interoperability between Web services, I mean that you can call J2EE Web services from a .NET client application and the reverse is also true. Analysts have projected that J2EE and .NET will each have approximately 40% of the Web services that will be implemented on their platform. This means that for a customer to be able to utilize all the Web services that will be available in the future, the platforms need to interoperate. Companies from both camps are working together to ensure interoperability. Several standards bodies have been formed to drive interoperability between the various Web services platform providers. For example, both SOAPBuilders and the Web Services Interoperability Organization (WS-I) are involved in interoperability efforts. To prove that simple J2EE and .NET Web services can interoperate, two things must be demonstrated. One, a .NET Web services client should be able to call and receive a response from a J2EE Web service, and two, a J2EE Web services client should be able to call and receive a response from a .NET Web service.

Case Study
In the interest of reusing code and knowledge, the same application was used to prove both points. I started with an existing J2EE application that uses servlets, JavaServerPages (JSPs), and Enterprise JavaBeans (EJBs) that I converted into a Web service. This application allows a field agent to view, edit, and create orders for a fictional construction company. The field agent is also able to sort existing orders by customer name,employee id, or order status.

Calling a J2EE Web Service from a .NET Client


Making Orders a J2EE Web Service In order to reuse as much code as possible, I used the EJBs that had been written and deployed on WebLogic Server to implement the business logic on the back end. First, I needed to create a Web service that exposed the methods of the EJBs. To create a Web service in BEA WebLogic Workshop, I used the wizard provided and named it Orders (see Figure 1). This generates a Web service file with the name Orders.jws. Using the WebLogic Server 7.0 and WebLogic Workshop Web service runtime environment, I added the EJBs to my Web service with controls. Controls are a mechanism in WebLogic Workshop for interacting with resources from within a Web service. They can be interfaces to databases, EJBs, JMS queues, or other Web services. Calling the EJBs from the Web Service In order to call the EJBs from my Web service in WebLogic Workshop, I need a handle to an EJB and this is what the control does. As you can see in the following code sample, the SearchEJB variable is my control; I call a particular method, getOrdersByID, and pass it the incoming parameter, ID. This is where the WebLogic Workshop tool really provides value to the developer. I didn't have to worry about getting references to the home and remote interfaces for the EJB. The control hides this complexity and simplifies the coding required to interface with the EJB. The jws:operation tag in the code sample is a signal to the WebLogic Server that this method is part of the Web service's public contract and is available for clients to invoke. /** * @jws:operation */ public ordermasterfields[] getOrdersByID(String ID) { ordermasterfields[] orderArray = null; try{ LinkedList orderList = SearchEJB.getOrdersbyID(ID); orderArray = getArray(orderList) } catch(Exception e){ System.out.println("*** In getOrdersByID method *** \n Exception is: " + e.getMessage()); } return (orderArray); } Once I added the EJBs to the Web service, I carefully checked the data types that were being returned from the EJB methods. I wanted to make sure that each of these data types is serializable so that I can return them over the wire to the Web service client. Notice in the code above that my EJB method returns

a LinkedList that is not serializable. There are two possible solutions to this problem. I could choose to either change the code in the EJB to return a serializable data type and redeploy the EJB, or I could convert the data type inside the Web service before returning it to the client. I chose the second option for simplicity: I didn't have to redeploy the EJBs and I could still use the original front-end Web application if necessary. Since LinkedLists are not serializable, I converted the Web service method to return an array of "ordermasterfields". This class, ordermasterfields, is user-defined and will be included in the WSDL document so the client knows what the user-defined types are. The getArray method performs the conversion. The next step is to generate a WSDL document for use by a .NET client. In WebLogic Workshop I simply clicked on the "Generate WSDL Document" button and it automatically generated the WSDL document for me. I'm now ready to create a client that will use the Orders J2EE Web service. Notice that all we've really done is add a new Web service interface to the EJBs. As a matter of fact, the Web application front end that I started with is still usable since the EJBs have not been changed. In addition to returning HTML from the original Web application, we are now returning an XML document that contains an array of Orders via SOAP from our Web service.

Creating a .NET Client for the J2EE Orders Web Service


Now that I've created a J2EE Web service I need to create a .NET client that will access it. In Visual Studio .NET, the first step is to import a WSDL document into the ASP.NET Web services client by adding a Web reference to the client through a wizard in Visual Studio .NET. You can either point to a file on your local system or to the URL of a Web service on the Internet. I added the Web reference for the J2EE Orders Web service and Visual Studio .NET automatically generated a client proxy file. This file maps all the operations and data types from the WSDL document to a client proxy code file, Reference.cs, on the client side. Then, all that is necessary is to create a variable that uses the client proxy to call methods on the Web service. The following code fragment contains excerpts from the Reference.cs file that show where to find the Web service and defines return types for a particular method. public Orders() { this.Url = "http://localhost:7001/Orders/Orders.jws"; } public ordermasterfields[] getOrdersByID(string ID) { object[] results = this.Invoke("getOrdersByID", ne object[] {ID}); return ((ordermasterfields[])(results[0])); } Now that I have added the Web reference and Visual Studio .NET has created a client proxy, I need to create an instance of the Web reference, Orders, and invoke a method. This is all the code I need to write to invoke a particular method on the Orders Web service. using OrderWebApplication.WebReference1;

WebReference1.Orders orders = new WebReference1.Orders(); WebReference1.ordermasterfields[] ordersResult = orders.getOrdersByID(userName); .NET has a concept of strict separation between the presentation and the implementation of code. They use what they call a code-behind file that is tied to the WebForm page that contains all the code (see Figure 2). All of the presentation is handled in the WebForm.aspx file, which is an ASP.NET page, and all code is written in the WebForm.aspx.cs file. The idea is the same as a JSP file where HTML is the presentation or static content and dynamic content is inserted in JSP tag sections. .NET simply extended that and moved the embedded code sections to a separate code file. I could still implement the ASP.NET page with combined HTML and ASP.NET code in the same file if I wanted to. I also created new presentation code with ASP.NET pages that duplicates what was already available on the J2EE platform, but this is not covered in this article. Request and Response Code Now that I have both a client and a Web service, let's look at what happens when I make a request to the Web service. With Web services, SOAP request and response messages are sent back and forth either asynchronously or synchronously. Here I show only the portion of the SOAP message that pertains to the Web service method I am invoking. I am calling the getOrdersByID method and passing it an ID parameter with a value of JANE.

The Client-Side Programming Models


The JAX-RPC client-side programming models can be used by Java applications, servlets, JSPs, JAX-RPC service endpoints (JSEs), and EJBs to exchange SOAP messages with remote Web service endpoints on any platform. JAX-RPC defines three client-programming models: generated stub, dynamic proxy, and DII (Dynamic Invocation Interface). While all three are available for you to use, you're most likely to use generated stubs in your work.

9.2.1 Generated Stubs


For a client to use JAX-RPC to access a Web service endpoint, the endpoint must publish a WSDL document. The JAX-RPC compiler, which is provided by your J2EE vendor, generates Java remote interfaces and stubs that implement the endpoint operations described by the WSDL document. Once the stubs and interfaces are generated, you can bind them to the JNDI ENC (Environment Naming Context) of any J2EE component and use them to communicate with the endpoint. In this model, the stubs are generated at deployment time (see Figure 9-2).

Figure 9-2. JAX-RPC Stub Generation Sequence Diagram

WSDL describes the interfaces to Web service endpoints using portType definitions; each portType may have one or more operation elements. WSDL portType and operation elements are analogous to Java interfaces and methods, respectively. In fact, JAX-RPC defines a mapping between WSDL and Java that generates remote interfaces from ports, with methods that correspond to port operations. For example, a WSDL document might describe a port called BookQuote with a single operation called getBookPrice as shown in Listing 9-4.

Listing 9-4 WSDL portType and message Definitions <message name="getBookPrice"> <part name="string" type="xsd:string"/> </message> <message name="getBookPriceResponse"> <part name="result" type="xsd:float"/> </message> <portType name="BookQuote"> <operation name="getBookPrice"> <input name="isbn" message="mh:getBookPrice"/> <output message="mh:getBookPriceResponse"/> </operation> </portType>
At deployment time a JAX-RPC compiler converts the WSDL portType into a corresponding remote interfacethe endpoint interface. The JAX-RPC compiler also generates a stub that implements the endpoint interface according to the port and binding definitions. It can also create a factory for accessing the stub that conforms to the WSDL service definition. The endpoint and service interfaces would look like those in Listings 9-5 and 9-6.

Listing 9-5 An Endpoint Interface Generated by the JAX-RPC Compiler public interface BookQuote extends java.rmi.Remote { public float getBookPrice(String isbn) throws java.rmi.RemoteException; } Listing 9-6 A Service Interface Generated by the JAX-RPC Compiler public interface BookQuoteService extends javax.xml.rpc.Service{ public BookQuote getBookQuotePort( ) throws java.rmi.RemoteException; }
Once the endpoint interface and stub have been generated, they can be used at runtime to invoke operations on the Web service endpoint. The code fragment shown in Listing 9-7 demonstrates how a J2EE

component such as a servlet, application client, or EJB uses a JAX-RPC generated stub to get the wholesale price of a book from a .NET Web service.

Listing 9-7 Using a Generated Stub in a J2EE Component InitialContext jndiContext = new InitialContext ( ); BookQuoteService service = (BookQuoteService) jndiContext.lookup("java:comp/env/service/BookQuoteService"); BookQuote bookQuote = service.getBookQuotePort(); float price = bookQuote.getBookPrice( isbn );
When the getBookPrice() method is invoked, the JAX-RPC stub sends a SOAP message to the Web service endpoint, in this case a .NET program. The .NET endpoint then processes the SOAP message and sends a response back to the stub. The stub extracts the result from the SOAP message and returns it to the client (see Figure 9-3).

Figure 9-3. JAX-RPC Invocation Loop

The generated stubs can include methods with primitive argument types like int and long, primitive wrappers like java.lang.Integer and java.lang.Long, arrays, a few standard Java types such as String and Date, custom object types, and special holder types. Holder types allow the JAX-RPC stubs to model INOUT and OUT parameter modes. Generated stubs are addressed in detail in Section 12.1.

9.2.2 Dynamic Proxies


In addition to generated stubs, JAX-RPC also supports dynamic proxies. A dynamic proxy is used in the same way as a generated stub, except the remote interface's stub implementation is generated dynamically, at runtime. If you're familiar with the Proxy architecture of the Java Reflection API, then dynamic JAX-RPC will make sense to you. The code fragment shown in Listing 9-8 demonstrates how a J2EE component such as a servlet, application client, or EJB uses a JAX-RPC dynamic proxy to get the wholesale price of a book from some Web service.

Listing 9-8 Using a Dynamic Proxy in a J2EE Component InitialContext jndiContext = new InitialContext ( ); BookQuoteService service = (BookQuoteService) jndiContext.lookup("java:comp/env/service/DynamicService"); BookQuote BookQuote_port = (BookQuote)service.getPort(BookQuote.class); float price = BookQuote_port.getBookPrice( isbn );
At runtime the getPort() method automatically maps the BookQuote interface to a corresponding port definition in the WSDL document, then generates a stub for the interface that implements the protocol defined by the associated binding. Dynamic proxies are covered in Section 12.2: Dynamic Proxies.

9.2.3 DII

JAX-RPC also provides another, even more dynamic API, called the Dynamic Invocation Interface. DII allows the developer to assemble SOAP method calls dynamically at runtime. If you have used the CORBA Dynamic Invocation Interface, then JAX-RPC DII will be a familiar concept. JAX-RPC DII is kind of like Java Reflection. It enables you to get a reference to an object that represents a Web service operation, in the form of a method, and to invoke that method without having to use a stub or a remote interface. For example, the following fragment of code demonstrates how a J2EE component uses JAX-RPC DII to get the wholesale price of a book from some Web service.

InitialContext jndiContext = new InitialContext ( ); javax.xml.rpc.Service service = (javax.xml.rpc.Service) jndiContext.lookup("java:comp/env/service/DynamicService"); QName port = new QName("http://www.xyz.com/BookQuote ","BookQuote"); QName operation = new QName("http://www.xyz.com/BookQuote","getBookPrice"); Call callObject = service.createCall(port, operation); Object [] parameters = new Object[1]; parameters[0] = isbn; Float price = (Float) callObject.invoke( parameters );
You can actually configureat runtimethe parameters, invocation style, encoding, and so on. Everything you can configure statically with WSDL you can configure dynamically with DII. DII is covered in detail in Section 12.3. While it is certainly useful for self-organizing systems and IDEs, the majority of J2EE developers will use generated stubs for access to Web services. In most cases, you will know in advance the Web service endpoints and specific operations you will be accessing, so the DII will not be necessary.

Vous aimerez peut-être aussi