Sample Code


The sample code in this chapter will show how WS-Routing can be used to configure a routing path for both a .NET and Java client, both of which will be calling a .NET and Java Web service.

For the Web services, we'll use a stock-pricing example, similar to the one shown in Chapter 11, "Asynchronous Interoperability, Part 4: BizTalk Server." This Web service offers a single method to return the price of a stock based on a given symbol.

 public double getPrice(String ticker) 

For example, a client could pass a ticker symbol for "TREY", and the Web service would return the latest price (for example, 14.45).

To show the routing elements of the sample, we will configure and use an intermediary that will reside between the client and server portions of the sample. This setup is shown in Figure 15.2.

click to expand
Figure 15.2: Message request and flow for sample code.

As we can see, both the .NET and Java clients will use the intermediary to reach either the .NET or Java Web service. (The Web service used will depend on the route taken.) The route will be configured by the client, and the intermediary itself will be hosted as a .NET Web service.

The return value (the price quote) will be passed back to the client by using a reverse route as shown in Figure 15.3.

click to expand
Figure 15.3: Message response and flow for sample code.

Setting Up the .NET Intermediary

The intermediary is configured as a .NET Web service and, as such, requires that a virtual directory be created. To create this virtual directory, launch Internet Information Services (IIS) Manager from the Start/Programs/Administrative Tools program group .

Within the administration tool, expand the tree and right-click the Default Web Site entry. Select New/Virtual Directory. For the alias of the virtual directory, enter Router . This alias is used by configuration files for both the .NET and Java clients. Click the Next button. For the directory, enter C:\Interoperability\Samples\Advanced\WS-Routing\dotNET\Intermediary . Click Next, and accept the rest of the defaults to complete the wizard.

The dotNET\Intermediary directory contains two files: Web.config and referralCache.config. Web.config is used to configure an HttpHandler that is going to process the routing request, shown in this section of the file:

 <httpHandlers>     <add verb="*" path="router.asmx"          type="Microsoft.Web.Services.Routing.RoutingHandler,               Microsoft.Web.Services, Version=1.0.0.0,                Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </httpHandlers> 

This means any request sent to http://localhost/Router/router.asmx will be processed by the RoutingHandler itself. As the handler deals only with SOAP requests , this can't be demonstrated through a browser.

The referralCache.config file isn't something we will be using in this sample, but it will be discussed in the "WS-Referral" section later in this chapter.

Once the virtual directory is created, the intermediary is ready to route messages.

Setting Up the .NET Web Service

To activate the .NET Web service, we will create a second virtual directory. Before creating this directory, compile the sample code in C:\Interoperability\Samples\Advanced\WS-Routing\dotNET\WebService. After this is done, use the same method shown in the previous section to create a virtual directory for this Web service. The alias of the virtual directory must be dotNETPriceService .

If you've run through the sample code outlined in Chapter 11, you might already have a virtual directory with this name . For this sample, you should delete the old virtual directory and re-create one using this new location. For the location of this virtual directory, use the folder containing the source ( C:\Interoperability\Samples\Advanced\WS-Routing\dotNET\WebService ).

Complete the steps in the wizard. After the virtual directory has been created, verify the Web service by browsing to http://localhost/dotNETPriceService/PriceService.asmx , as shown in Figure 15.4.

click to expand
Figure 15.4: Testing the Pricing Web service by using Microsoft Internet Explorer.

To test the Web service, you can invoke the getPrice method by using the browser and specify a test parameter (WOOD, for example) to ensure that a valid value is returned.

Setting Up the Java Web Service

The Java Web service is a replica of the .NET Web service, offering stock prices based on the same list of ticker symbols. Using the same method and type names allows both the .NET and Java clients to easily "switch" between the two without using a separate proxy file.

To configure and run the Java Web service, navigate to the C:\Interoperability\Samples\Advanced\WS-Routing\Java\WebService directory. Use the Ant script to build and publish the Web service. The published Web Services Description Language (WSDL) can be verified at http://localhost:8004/JavaPriceService/PriceService.wsdl .

Running the .NET Client

We now have the two stock-price Web services configured, and we have an intermediary set up to correctly route messages. Let's now test them by using one of the clients.

The sample code for the .NET client can be found in the C:\Interoperability\Samples\Advanced\WS-Routing\dotNET\Client directory. We'll build the client, but before we run it, let's first look at how the code is going to work.

The proxy class used for both the .NET and Java Web service can be found in PriceService.cs. As with the other WSE clients in the previous two chapters, this class has been pregenerated using WSDL, the class now inherits from WebServicesClientProtocol , and a reference to the Microsoft.Web.Services namespace is included. Notice how the URL of the proxy is currently set to the .NET Pricing Web service. We'll be setting the URL and the route as part of the client, which will determine whether to keep the current .NET Web service reference or to use the Java Web service instead.

The client first creates a new reference to the Web service, as normal.

 PriceService ps = new PriceService(); 

After this, the URL of the service is set. There are two options in the code ”one for the Java Web service URL, and the other for the .NET Web service URL:

 //ps.Url = "http://localhost:8004/JavaPriceService/PriceService"; ps.Url = "http://localhost/dotNETPriceService/PriceService.asmx"; 

As shown here, currently the URL is referencing the .NET Web service, with the reference to the Java Web service being commented out. This URL sets the end point of the route, and in this sample, it does not change during the lifetime of the call.

The route that the message will take will now be constructed . This is done by first obtaining the SoapContext of the Web service proxy request. With this context, we can then define a forward path (one that goes via the .NET intermediary) and ensure that the reverse path details are constructed when the message is routed.

 SoapContext ctx = ps.RequestSoapContext; ctx.Path.Fwd.Add(new Via(     new Uri("http://localhost/Router/router.asmx"))); ctx.Path.Rev = new ViaCollection(); 

Finally, the call to the pricing Web service is made for a quote of a particular stock.

 Console.WriteLine("Price of TREY = "+ps.getPrice("TREY")); 

Build and run the sample code using the provided NAnt script. If all is successful, the price for the TREY stock will be returned.

 Price of TREY = 9.35 

To prove that routing really is working in this sample, we can enable tracing at the .NET intermediary. Doing this will show the messages being sent from the .NET client as they are handled by the router and forwarded to the .NET Web service.

Tracing is enabled on the .NET intermediary by modifying the Web.config file in the C:\Interoperability\Samples\Advanced\WS-Routing\dotNET\Intermediary directory. Within this configuration file is a line that enables/disables tracing:

 <trace enabled="false" input="input.log" output="output.log"/> 

Set the trace enabled setting to true , and rerun the sample code. This will create an input.log file for the messages received by the intermediary and an output.log file for those sent to the Web service.

Note

If after you run the sample the log files are not created, check the permissions of the dotNET\Intermediary directory. Ensure that the ASPNET account (or the account the .NET Web service is running under) has read and write permissions to this directory. You can double-check this by looking at entries generated in the application log.

In the input.log file, we can observe two messages, both wrapped in < soap:Envelope > elements. The first message is from the Web services client to the router.

In this first message, the < wsrp:action > field is the fully qualified name of the Web service method call. This is the request from the client.

 <wsrp:action>     http://www.microsoft.com/interoperability/ws-routing/getPrice </wsrp:action> 

Next, the < wsrp:to > element indicates the intended destination for the Web service call. In this case, it is an endpoint, which is the .NET PriceService Web service.

 <wsrp:to>     http://localhost/dotNETPriceService/PriceService.asmx </wsrp:to> 

The < wsrp:fwd > element contains the route, which for our code sample is the address of the intermediary:

 <wsrp:fwd>     <wsrp:via>http://localhost/Router/router.asmx</wsrp:via> </wsrp:fwd> 

Notice also how the < wsrp:rev > element is currently empty:

 <wsrp:rev /> 

Because the message has not passed through any routers yet, no reverse path defined. The final part of the header is a timestamp. This contains a creation and expiration time. (The expiration time can be adjusted on the client.)

 <wsu:Timestamp      xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">     <wsu:Created>2003-05-02T04:48:26Z</wsu:Created>     <wsu:Expires>2003-05-02T04:53:26Z</wsu:Expires> </wsu:Timestamp> 

Now open the output.log file. In this log file, there are again two messages. The first message is the one the router constructed for the .NET Web service. Again, we have the timestamp as part of the header, but the router has included a < wsu:Received > element, which indicates where the message was received and the type of delay the message was subject to (between the client sending and the router receiving).

 <wsu:Received      Actor="http://localhost/Router/router.asmx"      Delay="696">2003-05-02T04:48:26Z </wsu:Received> 

This delay is useful in multihop environments, as (with a little additional logic) routes for Web services could potentially be optimized based on network traffic or utilization.

For the route, we now see that the < wsrp:to > address remains the same, and this time there is no < wsrp:fwd > path.

 <wsrp:to>     http://localhost/dotNETPriceService/PriceService.asmx </wsrp:to> 

Because the end of the path has been reached, the message can now be sent directly to the Web service. We can observe, however, that the router has created (or added to) the reverse path.

 <wsrp:rev>   <wsrp:via>http://localhost/Router/router.asmx</wsrp:via> </wsrp:rev> 

The < wsrp:via > element means that once a reply is received from the Web service, it will be first passed back to the router (which then has to work out how to send it back to the originating client).

Let's go back to the input.log file. The second message (again, contained within the < soap:Envelope >) is the response that comes back from the .NET Web service. This message contains the < getPriceResult >, which is the price of the stock. Notice how there are no < wsrp > elements in this second message. Because this is a request/response type of call (using HTTP), the connection from the router to the Web service is never dropped. You can imagine, however, that in a solution based on a more asynchronous model this path would play a greater role.

Finally, let's return to the second message in the output.log file. This message is a forward of the result from the Web service, with a timestamp header to indicate the delay between the message being delivered to the Web service and a result being returned.

If you have reached this stage, congratulations! You have now sent and traced your first message using WS-Routing. Let's switch the path so that the .NET client now routes to the Java Web service. Doing so is easy, and it involves editing the Client.cs file in the dotNET\Client subdirectory.

Within this file, comment out the URL for the .NET Web service and uncomment the URL for the Java Web service. After doing this, the two URL lines should look as follows :

 ps.Url = "http://localhost:8004/JavaPriceService/PriceService"; //ps.Url = "http://localhost/dotNETPriceService/PriceService.asmx"; 

Save this file, and rebuild the client. Also ensure that the Java Web service is running and is able to accept requests. Rerunning the client shows the same result, this time delivered from the Java Web service.

 Price of TREY = 9.35 

Examining the input.log and output.log files reveals a very similar trace to the .NET Web service, except now the Java Web service is being called.

Running the Java Client

Let's now take a look at the Java client. Looking at the main class (Client.java, which is found in C:\Interoperability\Samples\Advanced\WS-Routing\Java\Client), we can see that as with the .NET client, we have two instances of the URL:

 String url      = "http://localhost:8004/JavaPriceService/PriceService.wsdl"; //String url  //    = http://localhost/dotNETPriceService/PriceService.asmx?WSDL"; 

In this instance, the URL to the Java Web service is uncommented and will be used first.

To set the route for the client, a ProxyContext ( electric.glue.context.ProxyContext ) is used. The ProxyContext class specifies an addForwardIntermediary method, which sets the forwards as appropriate.

 ProxyContext context = new ProxyContext(); context.addForwardIntermediary(     "http://127.0.0.1/Router/router.asmx"); 

With this context, a new instance of the Web service proxy is then created, and the getPrice method is called to initiate the call.

 IPriceService priceService       = (IPriceService)Registry.bind(url,          IPriceService.class,context); double price = priceService.getPrice("WOOD"); System.out.println("Price of WOOD = "+price); 

Build and run the sample using the provided Ant script. This time, using the GLUE libraries, the Java client will compose a Web service request to the Java Web service, but with the path containing the .NET intermediary. If you look at the recent entries in the input.log file generated by the router, you will observe the following:

 <rp:to>http://localhost:8004/JavaPriceService/PriceService</rp:to> <rp:fwd>     <rp:via>http://127.0.0.1/Router/router.asmx</rp:via> </rp:fwd> 

As shown, the actual destination is the address of the Java Web service, yet the router remains as the .NET intermediary.

With the GLUE libraries, one useful logging feature is the ability to log the incoming or outgoing SOAP requests to the console. On either the Java Web service or client, this can be enabled by using the following line:

 electric.util.log.Log.startLogging("SOAP"); 

Given that both the Web services used the same method and return the same results, logging Web service invocations can also be a neat way of finding out whether the result is coming back from the .NET or Java Web service.

Using the Same Proxy File

In both the .NET and Java client, you might have noticed that a single proxy file was used for both the .NET and Java Web services. On the client, doing this allows us to use ps.URL = { } to easily switch between the two.

Ensure that both Web services are compliant to the Web Services Interoperability Organization (WS-I) basic profile by using document/literal-type encoding. For the .NET Web service, this will be the default. For the Java Web service, this is set manually in the WS-Routing example by adding a call to context.setDocumentStyle in the Publish.java file. By modifying the GLUE global config.xml file, this change can be applied to other projects.

Using the same Web services proxy file is something we haven't covered in previous chapters, but that's a topic very applicable to interoperability scenarios using WS-Routing. To enable using the same proxy file, I always recommend three steps:

  1. Use Doc/Literal for both Web services.

  2. Agree on the target namespace and element names. To share the same proxy file, the target namespace and element names within the WSDL must be the same. For the .NET Web service, the namespace is set using the WebService attribute at the start of the class. The element names automatically use the names of the parameters in the WebMethods . For the Java Web service, the namespace is set in the Publish.java file. The context is used to set the target namespace with the following line: context.setProperty("targetNamespace","namespace")

    To ensure that the element names match the name of the parameters, the Web service must be run in debug mode. In the WS-Routing sample, this requirement is set in the Ant build.xml script.

  3. Allow considerations for more complex data types. In the WS- Routing example, primitive types were used for the getPrice Web method. The method accepted a String and returned a Double ” both values easily map to the equivalents in XSD. If you are looking to used a shared proxy file, consider the types of data exposed in the WSDL. Although both the .NET and Java type might have equivalents in XSD, their implementation is sometimes slightly different (and therefore would require a little manual intervention). A good example of this was with the ArrayList , as demonstrated in Chapter 5, "Connectivity with XML Web Services, Part 1." The ArrayList is returned by default with the .NET Web services, whereas an .elements field has to be called for the equivalent in GLUE.




Microsoft. NET and J2EE Interoperability Toolkit
Microsoft .NET and J2EE Interoperability Toolkit (Pro-Developer)
ISBN: 0735619220
EAN: 2147483647
Year: 2003
Pages: 132
Authors: Simon Guest

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net