Developing the Web Services

printer-friendly version of this section  Print  e-mail this section  E-Mail  add a public, group or private note  Add Note  add a bookmark about this section  Add Bookmark    

Java APIs for XML Kick Start
By Aoyon Chowdhury, Parag Choudhary

Table of Contents
Chapter 12.  Case Study: Deploying a Web Service Using JAX


You will remember from Chapter 11 that developing a Web service is a fairly involved process. For the four Web services in the case study, you will reuse most of the work that you did for Chapter 11. The only changes you'll need to make are the following:

  • Modify the getCarPartRequest method of the CarPartRequestImpl.java implementation file for each of the Web services to return XML data as a string to the calling application.

  • Modify the serviceName entry of the CarPartRequest_Config.properties file to reflect the name of the service. Remember that this file is created by the xrpcc tool, and among other details, also contains the service name by which the Web service is identified. In real-life scenarios, different companies will use different Web servers to host their Web services, so the file generated by the xrpcc tool can (and should) be used as is. However, in this case study, all the Web services are to be hosted on the same Web server (localhost), so it's necessary to ensure that each Web service has a unique service name identifier.

This is possible because all the suppliers implement the same interface. To reuse the code already developed in Chapter 11 and make the modifications suggested previously, you will do the following:

  1. Create a folder with the same name as the supplier name. In this folder, extract the contents of the CarPartRequest.war Web application archive file.

  2. Create a folder called workarea. In this folder, copy over the CarPartRequestIF.java and CarPartRequestImpl.java files.

  3. Modify the CarPartRequestImpl.java file and compile it.

  4. Copy over the compiled file into the <supplier_name>\WEB-INF\classes\ CarPartReq location. This will overwrite the older implementation file.

  5. Modify the config.properties file.

  6. Create the WAR file and deploy it in Tomcat.

You will begin by creating the Web service for The Best Deals on Accessories Inc. First, create two folders called BestDealsonAccessories and workarea.

Next, extract the contents for the CarPartRequest.war Web application archive file into this folder. To do this, enter the following command at the command prompt (remember to change the drive and directory name appropriately):

jar xvf d:\jwsdp-1_0\webapps\CarPartRequest.war 

This will extract the Web application archive into the BestDealsonAccessories folder.

Next, copy over the CarPartRequestImpl.java file into the workarea folder. We want the getCarPartRequest method to return information about the car parts and accessories that it has as XML data. To do so, edit the CarPartRequestImpl.java and make the modifications shown in bold in Listing 12.5.

Listing 12.5 The CarPartRequestImpl.java File for The Best Deals on Accessories Inc.
package CarPartReq; public class CarPartRequestImpl implements CarPartRequestIF {         static String xmlString =                 "<?xml version=\"1.0\" encoding='us-ascii'?>"+                 "<carparts>"+                     "<supplier name=\"Best Deals on Accessories Inc.\" >"+                     "</supplier>"+                     "<engines>"+                         "<engine id=\"E129\" type=\"Alpha37\" capacity=\"2500\" price=\ graphics/ccc.gif"3500\">"+                             "Engine 1"+                         "</engine>"+                     "</engines>"+            "<carbodies>"+                         "<carbody id=\"C32\" type=\"Tallboy\" color=\"blue\">"+                             "Car Body 1"+                         "</carbody>"+                     "</carbodies>"+                     "<wheels>"+                         "<wheel id=\"W88\" type=\"X3527\" price=\"120\">"+                             "Wheel Set 1"+                         "</wheel>"+                     "</wheels>"+                     "<carstereos>"+                         "<carstereo id=\"C2\" manufacturer= \"MagicSound\" model=\"T76w\"  graphics/ccc.gifPrice=\"500\">"+                             "Car Stereo 1"+                         "</carstereo>"+     "</carstereos>"+     "</carparts>";     public String getCarPartRequest(String carparts1) {         if (carparts1.equals("carparts"))         {             return xmlString;         }         else         {             return new String ("Unable to process request. Wrong parameter supplied");         }     } } 

Now compile the file by specifying the following command:

javac -d . *IF.java javac -classpath . Impl.* 

Next copy CarPartRequestImpl.class into the D:\BestDealsonAccessories\WEB-INF\classes\CarPartReq location.

After copying over the updated implementation file, you need to modify the config.properties file. To do this, open the CarPartRequest_Config.properties file and make the change shown in bold:

# This file is generated by xrpcc. port0.tie=CarPartReq.CarPartRequestIF_Tie port0.servant=CarPartReq.CarPartRequestImpl port0.name=CarPartRequestIF port0.wsdl.targetNamespace=http://carpartsheaven.org/wsdl port0.wsdl.serviceName=BestDealsonAccessories port0.wsdl.portName=CarPartRequestIFPort portcount=1 

Now you need to create the WAR file and deploy it in the Tomcat Web server. To create the WAR file, go to the BestDealsonAccessories folder and enter the following at the command prompt:

jar cvf BestDealsonAccessories.war * 

This will create the BestDealsonAccessories.war file. Next you need to deploy this application in Tomcat. To do so, shut down the Tomcat Web server, copy over the BestDealsonAccessories.war file into the webapps folder, and restart Tomcat. To ensure that the application has been deployed correctly, launch a browser and enter the following URL:

http://localhost:8080/BestDealsonAccessories/jaxrpc 

The browser should display the following:

A Web Service is installed at this URL. It supports the following ports: "CarPartRequestIF" (http://localhost:8080/ graphics/ccc.gifBestDealsonAccessories/jaxrpc/CarPartRequestIF) 

Similarly, you need to create the Web services for the remaining three suppliers. Listings 12.6, 12.7, 12.8, 12.9, 12.10, and 12.11 show the CarPartRequestImpl.java and CarPartRequest_Config.properties files for Cool Accessories Inc., The Great Parts Supplier Inc., and We Supply Parts Inc.

Listing 12.6 The CarPartRequestImpl.java File for Cool Accessories Inc.
package CarPartReq; public class CarPartRequestImpl implements CarPartRequestIF {         static String xmlString =                 "<?xml version=\"1.0\" encoding='us-ascii'?>"+                 "<carparts>"+                     "<supplier name=\"Cool Accessories Inc.\" >"+                     "</supplier>"+                     "<engines>"+                         "<engine id=\"E129\" type= \"Alpha37\" capacity=\"2500\" price=\ graphics/ccc.gif"3500\">"+                             "Engine 1"+                "</engine>"+                     "</engines>"+                     "<carbodies>"+                         "<carbody id=\"C32\" type=\"Tallboy\" color=\"blue\">"+                             "Car Body 1"+                         "</carbody>"+                     "</carbodies>"+                     "<wheels>"+                         "<wheel id=\"W88\" type=\"X3527\" price=\"120\">"+                             "Wheel Set 1"+                         "</wheel>"+                     "</wheels>"+                     "<carstereos>"+                         "<carstereo id=\"C2\" manufacturer= \"MagicSound\" model=\"T76w\"  graphics/ccc.gifPrice=\"600\">"+                             "Car Stereo 1"+                     "</carstereo>"+     "</carstereos>"+     "</carparts>";     public String getCarPartRequest(String carparts) {         if (carparts.equals("carparts"))         {             return xmlString;         }         else         {             return new String("Unable to process request. Wrong parameter supplied");         }     } } 
Listing 12.7 The CarPartRequest_Config.properties File for Cool Accessories Inc.
# This file is generated by xrpcc. port0.tie=CarPartReq.CarPartRequestIF_Tie port0.servant=CarPartReq.CarPartRequestImpl port0.name=CarPartRequestIF port0.wsdl.targetNamespace=http://carpartsheaven.org/wsdl port0.wsdl.serviceName=coolaccessories port0.wsdl.portName=CarPartRequestIFPort portcount=1 
Listing 12.8 The CarPartRequestImpl.java File for The Great Parts Supplier Inc.
package CarPartReq; public class CarPartRequestImpl implements CarPartRequestIF {         static String xmlString =                 "<?xml version=\"1.0\" encoding='us-ascii'?>"+                 "<carparts>"+                     "<supplier name=\"Cool Accessories Inc.\" >"+                     "</supplier>"+                     "<engines>"+                         "<engine id=\"E129\" type= \"Alpha37\" capacity=\"2500\" price=\ graphics/ccc.gif"3500\">"+                             "Engine 1"+                         "</engine>"+                     "</engines>"+                     "<carbodies>"+                         "<carbody id=\"C32\" type=\"Tallboy\" color=\"blue\">"+                             "Car Body 1"+                         "</carbody>"+                "</carbodies>"+                     "<wheels>"+                         "<wheel id=\"W88\" type=\"X3527\" price=\"120\">"+                             "Wheel Set 1"+                         "</wheel>"+                     "</wheels>"+                     "<carstereos>"+                         "<carstereo id=\"C2\" manufacturer= \"MagicSound\" model=\"T76w\"  graphics/ccc.gifPrice=\"700\">"+                             "Car Stereo 1"+                         "</carstereo>"+     "</carstereos>"+     "</carparts>";     public String getCarPartRequest(String carparts) {         if (carparts.equals("carparts"))         {             return xmlString;         }         else         {             return new String("Unable to process request. Wrong parameter supplied");         }     } } 
Listing 12.9 The CarPartRequest_Config.properties File for The Great Part Supplier Inc.
# This file is generated by xrpcc. port0.tie=CarPartReq.CarPartRequestIF_Tie port0.servant=CarPartReq.CarPartRequestImpl port0.name=CarPartRequestIF port0.wsdl.targetNamespace=http://carpartsheaven.org/wsdl port0.wsdl.serviceName=greatcarpartsupplier port0.wsdl.portName=CarPartRequestIFPort portcount=1 
Listing 12.10 The CarPartRequestImpl.java File for We Supply Parts Inc.
package CarPartReq; public class CarPartRequestImpl implements CarPartRequestIF {         static String xmlString =                 "<?xml version=\"1.0\" encoding='us-ascii'?>"+                 "<carparts>"+                     "<supplier name=\"Cool Accessories Inc.\" >"+                     "</supplier>"+                     "<engines>"+                         "<engine id=\"E129\" type= \"Alpha37\" capacity=\"2500\" price=\ graphics/ccc.gif"3500\">"+                             "Engine 1"+                         "</engine>"+                     "</engines>"+                     "<carbodies>"+                         "<carbody id=\"C32\" type=\"Tallboy\" color=\"blue\">"+                             "Car Body 1"+                         "</carbody>"+                     "</carbodies>"+                     "<wheels>"+                    "<wheel id=\"W88\" type=\"X3527\" price=\"120\">"+                             "Wheel Set 1"+                         "</wheel>"+                     "</wheels>"+                     "<carstereos>"+                         "<carstereo id=\"C2\" manufacturer= \"MagicSound\" model=\"T76w\"  graphics/ccc.gifPrice=\"800\">"+                             "Car Stereo 1"+                         "</carstereo>"+     "</carstereos>"+     "</carparts>";     public String getCarPartRequest(String carparts) {         if (carparts.equals("carparts"))         {             return xmlString;         }         else         {             return new String("Unable to process request. Wrong parameter supplied");         }     } } 
Listing 12.11 The CarPartRequest_Config.properties File for Cool Accessories Inc.
# This file is generated by xrpcc. port0.tie=CarPartReq.CarPartRequestIF_Tie port0.servant=CarPartReq.CarPartRequestImpl port0.name=CarPartRequestIF port0.wsdl.targetNamespace=http://carpartsheaven.org/wsdl port0.wsdl.serviceName=wesupplyparts port0.wsdl.portName=CarPartRequestIFPort portcount=1 

You've now successfully created and deployed the Web services. Finally, you will need to create the client that will query the registry, get the details about the companies that deal in car parts and the Web services that they expose, and then execute their Web services.

The client application is basically a combination of the two client applications that you have developed before, namely the application that queries the registry, and the client application that performs RPCs on the Web service. It also uses the SAX parsing routines that you saw in the JAXP chapters (Chapters 3 6). Specifically, the application will query the registry for companies that deal with car accessories, get the Web services they expose, and then execute the Web services to get the required data.

The code for the client application is shown in Listing 12.12.

Listing 12.12 The CarPartRequestClient.java Application
package CarPartReq; import javax.xml.registry.*; import javax.xml.registry.infomodel.*; import java.net.*; import java.util.*; import javax.xml.rpc.Stub; import javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import org.xml.sax.ext.LexicalHandler; import java.io.StringReader; public class CarPartRequestClient extends DefaultHandler{   static Vector accessURI = new Vector();   Connection connection = null;     public static void main(String[] args) {       //Query the registry if (args.length < 1) {         System.out.println("Usage: runclient " +             "MyRegistryQuery string");         System.exit(1);       }       String queryString = new String(args[0]);       System.out.println("Query string is " + queryString);       CarPartRequestClient mrq = new CarPartRequestClient();      mrq.connect();      mrq.runQuery(queryString);      //Execute the web services            try {              CarPartRequestIF_Stub stub =                  (CarPartRequestIF_Stub) (new CarPartRequest_Impl(). graphics/ccc.gifgetCarPartRequestIFPort());              for(int i=0;i<accessURI.size();i++)              {                  stub._setProperty(                    javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,                    (String)accessURI.elementAt(i));                  String xmlData = stub.getCarPartRequest("carparts");                  mrq.parseXML(xmlData); }            } catch (Exception ex) {              ex.printStackTrace();            }     } public void connect()     {        String queryURL =                "http://localhost:8080/registry-server/RegistryServerServlet";              String publishURL =                "http://localhost:8080/registry-server/RegistryServerServlet";       String httpProxyHost = "";       String httpProxyPort = "8080";       Properties props = new Properties();       props.setProperty("javax.xml.registry.queryManagerURL",         queryURL);       props.setProperty("com.sun.xml.registry.http.proxyHost",         httpProxyHost);       props.setProperty("com.sun.xml.registry.http.proxyPort",         httpProxyPort);       try {         // Create the connection,         ConnectionFactory factory =             ConnectionFactory.newInstance();         factory.setProperties(props);         connection = factory.createConnection();         System.out.println("Connection created to the registry");       } catch (Exception e) {         e.printStackTrace();       }     }       public void runQuery(String qString) {             RegistryService rsvrc = null;             BusinessQueryManager bsnsqrymgr = null;             try {               // Get registry service and query manager               rsvrc = connection.getRegistryService();               bsnsqrymgr = rsvrc.getBusinessQueryManager();               System.out.println("Got registry service and " +                 "query manager");               // Specify the find qualifiers and name patterns               Collection findQualifiers = new ArrayList();               findQualifiers.add(FindQualifier.SORT_BY_NAME_DESC);               Collection namePatterns = new ArrayList();               namePatterns.add("%" + qString + "%"); // Find organization that match the name pattern containing the query string               BulkResponse response =                 bsnsqrymgr.findOrganizations(findQualifiers,                     namePatterns, null, null, null, null);               Collection orgs = response.getCollection();               // Display information about the organizations found               Iterator iter = orgs.iterator();               if (!(iter.hasNext())) {                 System.out.println ("There are no organizations which match the supplied  graphics/ccc.gifpattern");               } else while (iter.hasNext()) {                 Organization orgn = (Organization) iter.next();                 System.out.println("Organization name: " + orgn. getName().getValue());                 System.out.println("Organization description: " +                    orgn.getDescription().getValue());                 System.out.println("Organization key id: " + orgn. getKey().getId());                  //Display contact information such as contact person's name, phone  graphics/ccc.gifnumber etc                 User prmrycont = orgn.getPrimaryContact();                 if (prmrycont != null) {                     PersonName prmrycontName = prmrycont.getPersonName();                     System.out.println(" Contact name: " +                       prmrycontName.getFullName());                     Collection phoneNums =                       prmrycont.getTelephoneNumbers(null);                    Iterator phoneiter = phoneNums.iterator();                     while (phoneiter.hasNext()) {                       TelephoneNumber number =                         (TelephoneNumber) phoneiter.next();                       System.out.println("  Phone number: " +                         number.getNumber());                     }                     Collection emailadd = prmrycont.getEmailAddresses();                     Iterator emailiter = emailadd.iterator();                     while (emailiter.hasNext()) {                       EmailAddress eAd =                         (EmailAddress) emailiter.next();                       System.out.println("  Email Address: " +                         eAd.getAddress());                     }                 }                 // Display service and binding information                 Collection services = orgn.getServices();                 Iterator srvciter = services.iterator();                 while (srvciter.hasNext()) {                     Service srvc = (Service) srvciter.next();                     System.out.println(" Service name: " +                       srvc.getName().getValue());                     System.out.println(" Service description: " +                       srvc.getDescription().getValue());                     Collection serviceBindings =                       srvc.getServiceBindings();                     Iterator srvcbndgiter = serviceBindings.iterator();                     while (srvcbndgiter.hasNext()) {                       ServiceBinding sb =                         (ServiceBinding) srvcbndgiter.next();                       System.out.println("  Binding " +                         "Description: " +                         sb.getDescription().getValue());                      System.out.println("  Access URI: " + sb.getAccessURI());                         accessURI.add(sb.getAccessURI());                     }                 }                 // Print spacer between organizations                 System.out.println(" --- ");               }             } catch (Exception e) {               e.printStackTrace();             } finally  {               // At end, close connection to registry               if (connection != null) {                 try {                     connection.close();                 } catch (JAXRException je) {}               }       } } void parseXML(String xmlData) {     try{     /*Create a SAX Parser Factory*/         SAXParserFactory parseFactory = SAXParserFactory.newInstance();         /*Set to generate Validating SAX Parser */         parseFactory.setValidating(false);         /*Obtain a validating SAX Parser */         SAXParser saxParser = parseFactory.newSAXParser();         /*XML Reader is the interface for reading an XML document using callbacks*/         XMLReader xmlReader = saxParser.getXMLReader();         /*set the error handler*/         xmlReader.setErrorHandler(new MyErrorHandler());         /*Attach ContentHandler - the callbacks like startDocument, startElement etc. are           overridden by the setContentHandler to trap them into user code*/         xmlReader.setContentHandler(new CarPartRequestClient()); /*Parse an XML document - the document is read and overridden callbacks in the  graphics/ccc.gifMyXMLHandler are invoked*/             StringReader stringReader=new StringReader(xmlData);             xmlReader.parse(new InputSource(stringReader));         }         catch (SAXParseException saxException) { /* If there are errors in XML data are trapped and location is displayed*/                 System.out.println("\n\nError in CarParts.xml at line:"+ saxException. graphics/ccc.gifgetLineNumber()+"("+saxException.getColumnNumber()+")\n");                 System.out.println(saxException.toString());               }         catch (SAXException saxEx) {        /* If there are errors in XML data, the detailed message of the exception is  graphics/ccc.gifdisplayed*/                 System.out.println(saxEx.getMessage());             }         catch (Exception pce)         {             pce.printStackTrace(System.out);         }     }   public void startElement(String namespaceURI, String localName,                      String qualifiedName, Attributes elementAttributes)     {       if(qualifiedName.equals("Supplier")||qualifiedName.equals("carstereo"))       {             System.out.println(qualifiedName);             printAllAttributes(elementAttributes);       }     } public void printAllAttributes(Attributes elementAttributes)       {         System.out.println("\tTotal Number of Attributes: " +elementAttributes.getLength( graphics/ccc.gif));         for(int i=0;i<elementAttributes.getLength();i++)         {             System.out.println("\t\tAttribute: "+elementAttributes.getQName(i)+                                       " = "+elementAttributes.getValue(i));         }     } static class MyErrorHandler implements ErrorHandler     {       public void fatalError(SAXParseException saxException)       {         System.out.println("Fatal Error occurred "+ saxException);       }       public void error(SAXParseException saxException)       {         System.out.println("Error occurred "+ saxException);       }       public void warning(SAXParseException saxException)       {         System.out.println("warning occurred "+ saxException);       }     } } //end CarPartRequestClient 

NOTE

The code discussed so far is available in the case_study_code_samples folder.


To run the client, enter the following at the command prompt:

java -DuseSOAP=true -classpath .;d:\jwsdp-1_0\common\lib\jaxrpc-ri.jar; d:\jwsdp-1_0\ graphics/ccc.gifcommon\lib\jaxrpc-api.jar; d:\jwsdp- 1_0\common\lib\activation.jar;d:\jwsdp-1_0\common\ graphics/ccc.giflib\dom4j.jar; d:\jwsdp-1_0\common\lib\jaxm-api.jar; d:\jwsdp-1_0\common\lib\jaxm-client. graphics/ccc.gifjar; d:\jwsdp-1_0\common\lib\log4j.jar; d:\jwsdp-1_0\common\lib\mail.jar; d:\jwsdp-1_0\ graphics/ccc.gifcommon\lib\xalan.jar; d:\jwsdp-1_0\common\lib\xerces.jar; d:\jwsdp-1_0\common\lib\ graphics/ccc.gifjaxp-api.jar; d:\jwsdp-1_0\common\endorsed\dom.jar; d:\jwsdp-1_0\common\endorsed\sax.jar;  graphics/ccc.gifd:\jwsdp-1_0\common\endorsed\xalan.jar; d:\jwsdp-1_0\common\endorsed\xercesImpl.jar; d:\ graphics/ccc.gifjwsdp-1_0\common\endorsed\xsltc.jar; d:\jwsdp-1_0\common\endorsed\xalan.jar; d:\jwsdp-1_0\ graphics/ccc.gifcommon\endorsed\dom.jar; d:\jwsdp-1_0\common\endorsed\sax.jar; d:\jwsdp-1_0\common\ graphics/ccc.gifendorsed\xercesImpl.jar; d:\jwsdp-1_0\common\endorsed\xsltc.jar; d:\jwsdp-1_0\common\lib\ graphics/ccc.gifjaxr-ri.jar; d:\jwsdp-1_0\common\lib\jaxr-api.jar; d:\jwsdp-1_0\common\lib\jaxrpc-ri.jar;  graphics/ccc.gifd:\jwsdp-1_0\common\lib\jaxrpc-api.jar; d:\jwsdp-1_0\common\lib\activation.jar; d:\ graphics/ccc.gifjwsdp-1_0\common\lib\dom4j.jar; d:\jwsdp-1_0\common\lib\jaxm-api.jar; d:\jwsdp-1_0\common\ graphics/ccc.giflib\jaxm-client.jar; d:\jwsdp-1_0\common\lib\log4j.jar; d:\jwsdp-1_0\common\lib\mail.jar;  graphics/ccc.gifd:\jwsdp-1_0\common\lib\xalan.jar; d:\jwsdp-1_0\common\lib\xerces.jar; d:\jwsdp-1_0\ graphics/ccc.gifcommon\lib\jaxp-api.jar; d:\jwsdp-1_0\common\lib\commons-logging.jar; d:\jwsdp-1_0\common\ graphics/ccc.giflib\soap.jar; d:\jwsdp-1_0\common\lib\castor-0.9.3.9-xml.jar; d:\jwsdp-1_0\common\lib\ graphics/ccc.gifsaaj-api.jar; d:\jwsdp-1_0\common\lib\saaj-ri.jar;  CarPartReq.CarPartRequestClient  graphics/ccc.gifaccessories 

The output should be similar to what is shown in Listing 12.13.

Listing 12.13 Output of CarPartRequestClient Application
Query string is accessories Connection created to the registry Got registry service and query manager Organization name: Cool Accessories Inc. Organization description: Suppliers of high quality car accessories Organization key id: efa8b4e9-50ef-a8b4-2421-c64702085cf5  Contact name: James   Phone number: (800) 555-4444   Email Address: james@coolaccessories.com  Service name: CarPartRequest  Service description: Order Car Parts   Binding Description: Binding for CarPartRequest service   Access URI: http://localhost:8080/coolaccessories/jaxrpc/CarPartRequestIF  --- Organization name: Best Deals on Accessories Inc. Organization description: Suppliers of high quality car accessories Organization key id: efa8b510-50ef-a8b5-caff-1fd463029dc5  Contact name: Stephen   Phone number: (800) 555-55555   Email Address: stephen@bestdealsonaccessories.com  Service name: CarPartRequest  Service description: Order Car Parts   Binding Description: Binding for CarPartRequest service   Access URI: http://localhost:8080/BestDealsonAccessories/jaxrpc/CarPartRequestIF  --- carstereo        Total Number of Attributes: 4               Attribute: id = C2               Attribute: manufacturer = MagicSound               Attribute: model = T76w               Attribute: Price = 600 carstereo        Total Number of Attributes: 4               Attribute: id = C2               Attribute: manufacturer = MagicSound               Attribute: model = T76w               Attribute: Price = 500 

printer-friendly version of this section  Print  e-mail this section  E-Mail  add a public, group or private note  Add Note  add a bookmark about this section  Add Bookmark    
Top

[0672324342/ch12lev1sec5]

 
 


JavaT APIs for XML Kick Start
JAX: Java APIs for XML Kick Start
ISBN: 0672324342
EAN: 2147483647
Year: 2002
Pages: 133

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