So far, we've concentrated on some key aspects of Web services and seen how they can be implemented on both the .NET and Java platforms. To conclude the chapter's technical discussion, this section will pull together all the topics covered so far and show an extended example that builds upon what you've learned.
Figure 6.25 shows the two Web services that we've used throughout this chapter: the Buy Stocks Web service, based on the .NET platform, and the Sell Stocks Web service, based on the Java platform and using GLUE. These two services will play a key role in the chapter's final sample. To further illustrate interoperability between the .NET and Java platforms, we'll see a client that acts as a faade to these two Web services. This faade is based on ASP.NET and presents a Web-based UI representation of the two services.
The ASP.NET interface will touch on a number of points that have been introduced in the past few sections, including calling services from both .NET and Java; maintaining data type fidelity between the two platforms; and handling exceptions, authentication, and UDDI. In addition, as we step through the solution, I'll point out additional best practices and patterns that apply.
To install and run the solution, you'll need to have built the .NET and Java Web services in Chapter 5 and configured UDDI as described in the previous section.
The sample solution is located in the C:\Interoperability\Samples\Point\WebServices\Solution directory. To install the sample, first compile the code, and then within IIS Manager, create a new virtual directory that points to this folder. To create this virtual directory, follow the same directions that you used to create the sample .NET Web service at the start of Chapter 5. For this to run correctly, the alias for the virtual directory needs to be WebServiceSample .
After the virtual directory has been created, you need to set the IIS virtual directory security correctly in order to allow access to the UDDI registry. The UDDI registry that was configured in the previous section is, by default, set to allow Windows authentication. In order to pass the credentials to the UDDI registry, set the authentication mode for the WebServiceSample virtual directory to Integrated Authentication and remove Anonymous Access. You can set this by right-clicking the virtual directory, selecting Properties, and navigating to the Directory Security tab. This was covered earlier in this chapter in the "Web Services Authentication and Authorization" section. In addition, if you followed the samples in this section, you might want to check the security settings on the dotNETWebService virtual directory. Ensure that anonymous access is enabled.
The solution is now correctly installed and configured. To ensure that the sample can be run, start the Java Sell Stocks Web service by entering start ant run at a command prompt in the C:\Interoperability\Samples\Point\WebService\Java\WebService directory (which was shown at the end of Chapter 5).
After installing and configuring the solution, browse to http://localhost/WebServiceSample . This will display the home page for the solution, which Figure 6.26 shows.
The home page introduces the sample and presents two options to the user: Buy Stocks and Sell Stocks. In addition, the user is asked for the UDDI inquiry URL, which is prepopulated for a local instance of UDDI Services for Windows Server 2003.
Modify the UDDI inquiry URL as required for your own environment (for example, set the server to something other than localhost), and click the Buy Stocks option. The page used to buy stocks will be displayed, as shown in Figure 6.27.
Recall how in the chapter's earlier samples the Buy Stocks and Sell Stocks Web services contained two methods each ”one to get the list of recommendations, and the second to execute the actual order. In essence, ASP.NET is just a graphical representation of this.
On the Buy Stocks page, click the Get Recommendations button. In order to make this call, the ASP.NET page first makes a request to the UDDI server. By default, the ASP.NET page knows nothing about the location of the Buy Stocks Web service and relies on the UDDI directory for access point information. If you have a debug tracer attached to the ASP.NET process, it's possible to view the progress of the UDDI call.
Once the UDDI access point has been returned, ASP.NET UI then makes a call to the Buy Stocks Web service (which also is hosted via ASP.NET) and displays the list of recommendations as they're returned. Figure 6.28 illustrates this.
Select one of the recommendations, enter a valid quantity, and click the Buy button. Again, this will make another call to UDDI and return the address for the Web service. Then the BuyStocks method will be invoked.
In a production system, you'd probably consider caching the access point returned by the UDDI server, especially if you have an environment in which the Web service endpoints remain fairly static. Making a call to a UDDI server for each Web service request clearly has an impact on performance.
If all is successful, the ASP.NET page will report that the order was placed, presenting a confirmation in the Stock Advisor window, as shown in Figure 6.29.
To confirm this, recall how the .NET Web service shown earlier in this chapter writes all incoming orders to the Windows Event Log. If you check the application log, you should see an entry similar to the one shown in Figure 6.30.
Now let's look at how the call to the corresponding Java Web service is invoked. From the Web page, click the Sell Stocks button.
Again, click the Get Recommendations button. (See Figure 6.31.) This process uses the same code underneath but asks the UDDI server for the address of the Sell Stocks Web service, which will be resolved to our Java Web service.
As before, select a stock (this time to sell) and enter a quantity. If successful, the screen should display a confirmation message, as shown in Figure 6.32. If you look at the Java Web service as it's running in the background, you should see this message in the console window:
Incoming request to sell 50 common shares of Wingtip Toys (WING)
Although this is a basic solution, it shows a clean example of how you can abstract two Web services ”one from .NET and the other from Java ”and present them through a unified interface. By using a common interface and Web services to achieve interoperability between the two platform, you have the potential to build a unified interface for any number of disparate services. One advantage of doing this is that the user doesn't know whether the underlying connection is a Web service hosted in .NET or one hosted in Java. This comes back to some of the fundamentals outlined earlier in the book: the goal of interoperability is to present a user experience that doesn't necessarily expose the underlying number of disparate systems.
The project structure is relatively simple, and in looking through the code, you can see the approach that's been taken. The solution itself ”examined either from the command line or through Visual Studio .NET ”comprises a number of base pages: Default.aspx, BuyStocks.aspx, SellStocks.aspx, and Header.ascx. Header.ascx is an ASP.NET Web user control and is responsible for displaying a consistent banner at the top of each page you saw.
In addition to these pages, there are five subfolders :
Images Contains the JPEG image for the screen.
Datatypes Contains the shared stock type by using exactly the same schema as defined in Chapter 3, "Exchanging Data Between .NET and Java," where we looked at XSD and data types.
UDDI Contains a UDDILookup class that contains the commands for looking up an access point based on a service name .
Proxies Contains the proxies to call the Web services themselves .
Agents Acts as a layer between the pages and the proxies and is used to abstract the Web service proxy code away from the actual calling page, which we'll cover shortly.
Now that we have an overview of the solution, let's look at what happens when a user launches the sample and makes a request to either buy or sell some stocks.
Open the BuyStocks.aspx page. This page displays the controls required for buying a stock. Locate the BuyStockRecommendationsButton_Click method. This method is executed when the user clicks the Get Recommendations button.
BuyStocksAgent agent = new BuyStocksAgent(((String)Session["InquiryURL"])); ArrayList recommendations = agent.GetRecommendations();
After the button is clicked, a new instance of the BuyStocksAgent is created, which is passed the InquiryURL , which is the UDDI URL that's configured in the header of the page and stored in the ASP.NET session state. As you can see, the agent exposes a method named GetRecommendations .
Follow the code to the BuyStocksAgent.cs class. Here is the code that makes up the agent:
dotNETStockService ss = new dotNETStockService(); ss.Url = _accessPointURL; return new ArrayList(ss.GetRecommendations());
You can see how a new instance of the .NET Web service is created. Just before it's invoked, the URL is set. (The access point URL is obtained via UDDI in the constructor of the agent.) The UDDILookup class that's used to do this is based on the same sample code shown earlier. The agent then calls the "real" GetRecommendations method from the Web service.
Why do we use the agent layer? The agent service is a pattern that I like to adopt when creating solutions that invoke Web services. In essence, it's a layer that separates the underlying proxy for the Web service from the calling code. Separating this underlying proxy yields a number of advantages when calling Web services in general. We'll cover this momentarily, in the next section.
Finally, let's take a look at the proxy files that are used to call the Web service. Both files (dotNETStockService.cs and JavaStockService.cs) have been generated by using the WSDL.EXE tool. After they were generated, we made some modifications manually to get them to work neatly in the solution.
First, we added the WebServiceSample namespace to each file. (By default, WSDL.EXE doesn't add a namespace.) Second, we stripped out the definition for Stock from each file. This is really important. When WSDL.EXE was used to generate the proxy file, a stock type was also created (because it's exposed in the WSDL from the Web services). Unfortunately, as you create proxies for different platforms, you end up with multiple copies of the same type defined in the solution. To resolve this, you can rely on our standard definition for Stock (which is defined in portfolio.cs) and delete the definitions that are auto-generated by the tool. Because we're using a single version of the type definition, it's important to ensure that this type is attributed with a namespace that's shared by both Web services.
Finally, the proxy that's generated for the Java Web service contains a type named ArrayList , which is created and exposed by GLUE. To avoid confusion between this ArrayList and the ArrayList in the System.Collections namespace, this was renamed to GlueArrayList throughout the proxy file.
Hopefully, this extended sample pulls together some of the concepts we've addressed in the book so far. Feel free to step through this example in more detail to reaffirm some of the options and recommendations presented.