ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Working with Complex Data Types, Part 3
Pages: 1, 2, 3, 4, 5

The rest is pretty simple. We create an instance of StockTrade_ClientSide, taking advantage of the parameterized constructor to set its property values. Then we set up a Vector of parameters, just as we've done in earlier examples. If you run this example, you should see the following output:



Trade Description: Sell 350 of XYZ

Let's take a look at the SOAP envelope that was transmitted. The relevant part of the envelope begins with the trade element, representing the custom type parameter passed to the executeStockTrade service method. The value assigned to xsi:type is ns1:StockTrade. The ns1 namespace identifier is declared to be urn:BasicTradingService in the parent executeStockTrade element. And StockTrade is the name we specified for our custom type. There are three child elements of the trade element, each one corresponding to the properties of the StockTrade custom type. The name of each element corresponds exactly to the property name. This is crucial, as Apache SOAP is going to use Java reflection to find the set methods of the associated Java class. Therefore, the names in the envelope must match the names used by the class. Each one of these property elements is explicitly typed, and those types have to coincide with the types of the corresponding properties as well.


<SOAP-ENV:Envelope 
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"   
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <SOAP-ENV:Body>
     <ns1:executeStockTrade xmlns:ns1="urn:BasicTradingService"
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <trade xsi:type="ns1:StockTrade">
         <numShares xsi:type="xsd:int">350</numShares>
         <buy xsi:type="xsd:boolean">false</buy>
         <symbol xsi:type="xsd:string">XYZ</symbol>
      </trade>
     </ns1:executeStockTrade>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Now let's see how custom types are handled in GLUE, using the BasicTradingService class without modification. We need to add the executeStockTrade( ) method to the IBasicTradingService interface. We can deploy the service as we did before. Here's the new version of IBasicTradingService:


package javasoap.book.ch5;
public interface IBasicTradingService {
  int getTotalVolume(String[] symbols);
  String executeTrade(Object[] params);
  String[] getMostActive(  );
  String executeStockTrade(StockTrade_ClientSide trade);
}

Related Reading

Java and SOAP
By Robert Englander

Writing applications that access services has been easy using the GLUE API because we haven't dealt directly with the SOAP constructs. We should be able to follow that same pattern here, but there's a problem. The executeStockTrade( ) method of the IBasicTradingInterface interface says that the parameter is an instance of StockTrade_ClientSide. But the executeStockTrade method of the BasicTradingService class that implements the service uses a parameter of StockTrade_ServerSide. So there's a mismatch, albeit by design. By default, GLUE looks for the same class on the client and server sides. If we had used the StockTrade_ServerSide class in our client application instead of the StockTrade_ClientSide class, all would work perfectly. We're not going to do that, so just take my word for it that it works (or better yet, try it for yourself). We'll have to take another approach.

Whenever I develop distributed systems, I'm happy to share Java interfaces between both client and server code bases. However, I don't like to share Java classes, especially if those classes contain code that is specific to either the client or the server. In this example, we could rework the design of our stock trade classes to come up with something that contains the relevant data without any of the functionality of either the client or the server. We would want both the client and the server to use the class from the same package, so as the implementer of the basic trading web service, we'd have to distribute the package containing such a class to the developers' client applications. That would work, and it's not uncommon in practice. Getting back to the notion of sharing Java interfaces instead of classes, we could create an interface for the trade data, and have both the server and client classes implement that interface. That's a nice clean way to share between server and client code bases without sharing any actual executable code. Again, this interface would reside in a package available to both server and client systems. This mechanism is expected to be supported in a future version of GLUE (which might already be available by the time you read this).

GLUE does support another mechanism for making this stuff work. This mechanism doesn't require you to modify the server side, and the work involved on the client side is trivial. We're going to create a new package for this client example, because our work will create some files that have the same names as those we created earlier. The new package prevents us from overwriting files when running both client and server on the same machine. So be aware that this example is part of a new package called javasoap.book.ch5.client, and the files we'll be developing need to be in the corresponding directory for that package.

I've purposely avoided discussion of WSDL so far, even though GLUE is based entirely on WSDL. However, the first step in this process makes use of GLUE's wsdl2java utility, which generates Java code based on a WSDL file. So where does the WSDL come from? The GLUE server generates it automatically. The client-side applications based on the GLUE API have been taking advantage of this all along. wsdl2java generates the Java interface for binding to the service, a helper class, a Java data structure class that represents the data fields of the custom type we're working with, and a map file. The map file is essentially a schema definition for the custom type; it tells GLUE how to map the fields of the Java data structure class to the fields of the custom type. GLUE uses a number of mechanisms for handling custom type mapping; in this case, the map file defines the mapping explicitly, rather than basing the mapping on Java reflection.

So let's go ahead and generate the files for the client application. Enter the following command, making sure you are in the directory for the javasoap.book.ch5.client package:


wsdl2java http://georgetown:8004/glue/urn:BasicTradingService.wsdl 
   -p javasoap.book.ch5.client

The parameter to the wsdl2java utility is the full URL of the service WSDL, just like we've been using in the bind( ) methods in previous examples. The -p option tells the utility the name of the local package for which code should be generated: javasoap.book.ch5.client. The output from wsdl2java consists of four files. The first one is IBasicTradingService.java, which contains the Java interface for binding to the service. We've been writing this one by hand up until now; let's take a look at the one generated by wsdl2java:


// generated by GLUE
package javasoap.book.ch5.client;
public interface IBasicTradingService
  {
  String executeStockTrade( StockTrade_ServerSide arg0 );
  String[] getMostActive(  );
  int getTotalVolume( String[] arg0 );
  String executeTrade( Object[] arg0 );
  }  

Pages: 1, 2, 3, 4, 5

Next Pagearrow