So far, we have discussed at a conceptual level the object model defined by JAXR. Let us now look at the API that client applications use to interact with the registry and work with this model.
RegistryService is the main interface implemented by the JAXR pluggable provider layer. It allows the client to query the implementation for the capability level it supports and also obtain references to the three main interfaces from the underlying registry-specific providers. These are:
The BusinessLifeCyleManager interface, used for creating objects based on the information model
The BusinessQueryManager interface, used to query the registry using objects from the information model
The DeclarativeQueryManager interface, used to execute statement type queries on the registry
All objects in the information model are implemented as interfaces in JAXR. The underlying registry provider supplies the implementation classes. As Figure 12.12 shows, we will break up the API into four parts for discussion, along the lines of connecting to the registry, creating data items, finding data, and performing queries on the registry. Let us look at each of them in detail.
The first few step in any JAXR application is to establish a connection to the underlying registry, which is abstracted with a RegistryService interface. In general, all JAXR applications have the following sequence:
Create a ConnectionFactory.
Create a Connection object from that factory to the registry.
Pass the Connection the appropriate user credentials (e.g., username and password) required by the registry operator.
Obtain the reference to the RegistryService from the connection.
Do some work with the RegistryService.
JAXR uses the Factory pattern and a ConnectionFactory that can be configured with properties for initializing the underlying Connection object. Just like the JAXM factories discussed in Chapter 11, the JAXR factory can be obtained from the J2EE container's JNDI context, as shown below:
HashMap properties = new HashMap(); properties.put("some property", "some value"); // set other properties. InitialContext ctx = new InitialContext(properties); ConnectionFactory connfactory = (ConnectionFactory)ctx.lookup("java:comp/env/jaxr/connectionfactory");
Note that the JNDI mechanism can be used only if the container supports it. Tomcat and the reference implementation servlet container do not have this feature. EJB containers such as the J2EE reference implementation do have this feature.
Table 12.1 shows the properties specified by JAXR. The only required setting is the javax.xml.registry.queryManagerURL property; others are optional. The factory can also be instantiated by the default mechanism using the new-Instance() method, as shown below:
// Set the properties for the factory Properties environment = new Properties(); environment.setProperty("javax.xml.registry.queryManagerURL", QUERY_URL); environment.setProperty("javax.xml.registry.lifeCycleManagerURL", PUBLISH_URL); // Instantiate the factory and create a connection from it ConnectionFactory connfactory = ConnectionFactory.newInstance(); connfactory.setProperties(environment); Connection connection = connfactory.createConnection(); // Authenticate the username and password with the registry PasswordAuthentication passwdAuth = new PasswordAuthentication(uddiusername, uddipassword.toCharArray()); Set credentials = new HashSet(); credentials.add(passwdAuth); connection conn.setCredentials(credentials); // Obtain a reference to the registry service RegistryService registryservice = connection.getRegistryService();
Connection property |
Description |
---|---|
javax.xml.registry.queryManagerURL |
A required string that specifies the URL to the query manager service for the provider. |
javax.xml.registry.lifeCycleManagerURL |
An optional string that specifies the URL to the lifecycle manager service for the provider. |
javax.xml.registry.semanticEquivalences |
An optional set of tuples that specifies how two Concepts in two different ClassificationSchemes for the internal taxonomy may be considered equivalent (for example, microsoft-com:geoweb:2000:United States, microsoft-com:geoweb:2000:USA | microsoft-com:geoweb:2000:Netherland, microsoft-com:geoweb:2000:Holland) |
javax.xml.registry.security.authenticationMethod |
An optional string that may be used to tell the provider about the authentication method to use |
javax.xml.registry.uddi.maxRows |
An optional integer that tells UDDI registries the maximum number of rows that should be returned for find operations |
javax.xml.registry.postalAddressScheme |
The PostalAddress object in the information model has well-defined attributes (e.g., street, city, postal code), whereas some registries, such as the UDDI registry, may represent address attributes simply as a set of lines. This optional string property can be used to specify a ClassificationScheme (i.e., a postal address scheme) the provider uses to map the structured information in JAXR and unstructured information in the underlying registry. |
The second part of the JAXR API deals with how applications can work with and manipulate objects in the information model with operations that can be used to create, update, delete, and save data in the underlying registry. The JAXR provider is responsible for translating these operations into the underlying registry's API calls.
The LifeCycleManager and the BusinessLifeCycleManager in the javax.xml.registry package shown in Figure 12.12 primarily abstracts all these operations, which are shown in Figure 12.13. The two lifecycle managers contain overloaded methods for:
Creating metadata entries such as Associations, Classification, ClasslificationScheme, and Concept
Creating data entries such as Organization, User, PostalAddress, Telephonenumber, Service, and ServiceBinding
Saving and deleting the Organization, Service, Concepts, Associations, and so on
Earlier in the chapter, we mentioned how everything in the information model is represented by an interface and that the implementation classes are vendor-provided. The BusinessLifeCyleManager is used as a factory for instantiating these objects in application classes. For example, empty User and TelephoneNumber objects can be created as shown below and subsequently populated with relevant attributes:
User contact = lifecyclemgr.createUser(); TelephoneNumber telnum = lifecyclemgr.createTelephoneNumber();
The third part of the API relates to searching the registry using get-find type operations. These operations relating to retrieving data from the underlying registry are abstracted by the QueryManager and the BusinessQueryManager interfaces in the javax.xml.registry package, as Figure 12.14 shows. Both these query managers contain overloaded methods that relate to different mechanisms for searching and retrieving information on existing data in the registry. The data could relate to Associations, Organizations, Concepts, or other objects from the information model. The JAXR provider takes care of translating these operations into the underlying registry's API and generating the corresponding SOAP messages.
A registry can be queried for different fields (e.g., Organization, Concepts, ServiceBindings, Service, Associations) using the BusinessQueryManager interface. Each argument to the methods in this interface is of type java.util.Collection, which represents the following:
findQualifiers. Constants specified in the javax.xml.registry.FindQualifier interface that specify the find criteria (e.g., sorting, searching, etc.) For example, to search in a case-sensitive manner and have the results arranged in descending order, the qualifier parameter would look like this:
Collection findqualifier= new ArrayList(); findqualifier.add(FindQualifier. CASE_SENSITIVE_MATCH); findqualifier.add(FindQualifier. SORT_BY_NAME_DESC);
namePatterns. The wildcard pattern based on the syntax of the SQL LIKE clause to search on (e.g., %Flute%, %Flute, Flute, Flute%, etc.)
For example, to search for an organization whose name contains starts with "Flute" in a case-sensitive manner and have the results arranged in descending order, the code would look like this:
Collection findqualifier= new ArrayList(); findqualifier.add(FindQualifier. CASE_SENSITIVE_MATCH); findqualifier.add(FindQualifier. SORT_BY_NAME_DESC); Collection searchpattern = new ArrayList(); searchpattern.add("%Flute"); BulkResponse response = querymgr.findOrganizations(findqualifier,searchpattern, null, null, null, null);
classifications. The Classification objects to use during the find operation.
specifications. The javax.xml.registry.infomodel.Concept or javax.xml.registry.infomodel.ExtrinsicObject objects to use during the find operation.
externalIdentifiers. The javax.xml.registry.infomodel.ExternalIdentifiers objects to use during the find operation.
externalLinks. The javax.xml.registry.infomodel.ExternalLink objects to use during the find operation.
Typical use cases covered later in this chapter will show more detailed code using these parameters in the corresponding find operations.
Level 1 registries, such as the ebXML registry, expose clients with the ability to execute declarative queries against the registry (Figure 12.15). JAXR abstracts this using the Query and DelarativeQuery interfaces. JAXR at present supports only SQL-92 and the OASIS ebXML registry filter queries. An example of a simple query that returns all the organizations in the registry is shown below:
// obtain a connection here RegistryService registryservice = conn.getRegistryService(); DeclarativeQueryManager decquerymgr = registryservice. getDeclarativeQueryManager; String querystr = "SELECT * FROM Organization" Query query = decquerymgr.createQuery(Query.QUERY_TYPE_SQL, querystr); BulkResponse response = dqm.executeQuery(query);
The JAXR API provides a higher level of abstraction. There are essentially two ways to register the business and the services it provides with a UDDI registry. UDDI specifications require that a UDDI provider expose the registry though a Web-based HTML interface, such as that shown in earlier figures. In this section, we will show how the JAXR API can be used to register details about Flute Bank and its bill payment Web service in the UDDI registry.
From an information model perspective, here are the steps that need to be followed to create the conceptual representation:
Create an Organization object.
Create a User object with a TelephoneNumber and EmailAddress and set it as the primary contact for the Organization.
Classify the Organization. We will follow the NAICS classification scheme and assign the code and category description that fits Flute Bank: the high-level category of "Finance and Insurance," with a corresponding code of 52.
From an implementation perspective, the steps are straightforward:
Create a Connection and obtain a reference to the RegistryService.
Obtain a BusinessLifeCycleManager from the RegistryService.
Obtain a BusinessQueryManager from the RegistryService. This is used only to set the ClassificationScheme.
Create the information model, as outlined above. Save the organization in the registry by invoking the saveOrganizations(Collection organizations) method in the BusinessLifeCycleManager.
Parse the BulkResponse returned by the registry to obtain the unique key the registry assigned our service.
Close the connection to the registry.
Listing 12.1 shows the complete code for these steps. Upon successful registration, the registry assigns a unique key and discovery URL to Flute Bank, which are printed in the output of the example. The successful registration can be verified using the Web interface of the registry, shown in Figure 12.16.
import javax.xml.registry.infomodel.*; import javax.xml.registry.*; import java.util.*; import java.net.PasswordAuthentication; public class UDDIPublishOrg { private static final String QUERY_URL= "http://www-3.ibm.com:80/services/uddi/v2beta/inquiryapi"; private static final String PUBLISH_URL = "https://www-3.ibm.com:443/services/uddi/v2beta/protect/publishapi"; private static String uddiusername; private static String uddipassword; public static void main(String[] args) { if(args.length!=2){ System.out.println("Usage java UDDIPublish username uddipassword"); return; } uddiusername = args[0]; uddipassword = args[1]; try{ // Set the properties for the ConnectionFactory Properties environment = new Properties(); environment.setProperty("javax.xml.registry.queryManagerURL", QUERY_URL); environment.setProperty("javax.xml.registry.lifeCycleManagerURL",PUBLISH_URL); // Instantiate the factory and create a connection from it ConnectionFactory connfactory = ConnectionFactory.newInstance(); connfactory.setProperties(environment); Connection conn = connfactory.createConnection(); // Authenticate the username and password with the registry PasswordAuthentication passwdAuth = new PasswordAuthentication (uddiusername, uddipassword.toCharArray()); Set credentials = new HashSet(); credentials.add(passwdAuth); conn.setCredentials(credentials); // Obtain a reference to the RegistryService, the BusinessLifeCycleManager, and // the BusinessQueryManager RegistryService registryservice = conn.getRegistryService(); BusinessLifeCycleManager lifecyclemgr =registryservice.getBusinessLifeCycleManager(); BusinessQueryManager querymgr = registryservice.getBusinessQueryManager(); // Create an organization object Organization company = lifecyclemgr.createOrganization("Flute Bank"); InternationalString description = lifecyclemgr.createInternationalString("A fictitious bank used for examples in the book Java Web Services Architecture, "+ "published by Morgan Kaufman, ISBN 1-55860-900-8." + "The authors can be reached at webservicesbook@yahoogroups.com OR" + "www.javawebservicesarchitecture.com. "); company.setDescription(description); // Create a user object User contact = lifecyclemgr.createUser(); PersonName name = lifecyclemgr.createPersonName("John Malkovich"); contact.setPersonName(name); // Create and set the user's telephone number TelephoneNumber telnum = lifecyclemgr.createTelephoneNumber(); telnum.setNumber("1-800-FLUTE-US"); Collection phonenumbers = new ArrayList(); phonenumbers.add(telnum); contact.setTelephoneNumbers(phonenumbers); // Create and set the user's email address EmailAddress email = lifecyclemgr.createEmailAddress ("uddiadmin@flutebank.com"); Collection emaillist = new ArrayList(); emaillist.add(email); contact.setEmailAddresses(emaillist); // Set the user as the primary contact for the organization company.setPrimaryContact(contact); ClassificationScheme scheme = querymgr.findClassificationSchemeByName(null," ntis-gov:naics"); // Create the classification using the above scheme and pass the relevant category code and description Classification classification = (Classification)lifecyclemgr.createClassification(scheme, "Finance and Insurance", "52"); Collection classificationlist = new ArrayList(); classificationlist.add(classification); company.addClassifications(classificationlist); // Set the organization in the list of organizations // An organization list is a list of organizations, because a user could choose to publish multiple organizations Collection organizationlist = new ArrayList(); organizationlist.add(company); // make the final call to the registry and get a response BulkResponse response = lifecyclemgr.saveOrganizations(organizationlist); Collection exceptions = response.getExceptions(); // If there are no exceptions, the publish action was successful if (exceptions == null) { Collection keys = response.getCollection(); Iterator iterator = keys.iterator(); Key key = (Key) iterator.next(); String uid = key.getId(); System.out.println("The unique ID returned by the UDDI registry for the Organization is " + uid); company.setKey(key); } // This means exceptions occurred during the publish action else { Iterator iterator = exceptions.iterator(); while (iterator.hasNext()) { Exception exception = (Exception) iterator.next(); System.out.println("Exception occurred while saving to the registry: " + exception); } } // Finally, close the connection conn.close(); } catch (Exception exception) { System.out.println("General exception occurred: " + exception); } } }
Let us examine what happens under the hood when the example class UDDIPublish is executed. Publication calls to UDDI happen only as a set of authenticated operations where the authentication is token-based. The JAXR provider connects to the registry using SSL and verifies the username and password by making the get_authToken call. The registry returns the XML response with the authToken element. The request sent looks like this:
<?xml version="1.0" encoding="UTF-8"?> <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Body> <get_authToken generic="2.0" userID="fluteuddi" cred="fluteuddi" xmlns="urn:uddi-org:api_v2"/> </soap-env:Body> </soap-env:Envelope>
The response from the registry looks like this:
<?xml version="1.0" encoding="UTF-8"?> <authToken generic="2.0" xmlns="urn:uddi-org:api_v2" operator="www.ibm.com/services/uddi"> <authInfo>xyzi2892somesecuretoken</authInfo> </authToken>
The provider then sends another SOAP request, which includes the token and XML structure, based on the save_business UDDI operation:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"> <soap-env:Body> <save_business generic="2.0" xmlns="urn:uddi-org:api_v2"> <authInfo>xyzi2892somesecuretoken</authInfo> <businessEntity businessKey=""> <name xml:lang="en">Flute Bank</name> <description xml:lang="en">A fictitious bank used for examples in the book Java Web Services Architecture, published by Morgan Kaufman, ISBN 1-55860-900-8. The authors can be reached at webservicesbook@yahoogroups.com OR http://www.javawebservicesarchitecture.com. </description> <contacts> <contact> <description xml:lang="en"> The primary contact person for Flute web services </description> <personName>John Malkovich</personName> <phone useType="">1-800-FLUTE-US</phone> <email>uddiadmin@flutebank.com</email> </contact> </contacts> <businessServices> <businessService serviceKey=""> <name xml:lang="en">Billpayservice</name> <description xml:lang="en">A web service allowing online account holders to pay bills online</description> <bindingTemplates> <bindingTemplate bindingKey=""> <description>Flute Bank, beyond providing simple account-related services, also has the infrastructure to support online bill payments. The online bill payment system is offered to customers as a value-added service. </description> <accessPoint URLType="http"> http://127.0.0.1:8080/billpayservice/jaxrpc/BillPay </accessPoint> <tModelInstanceDetails/> </bindingTemplate> </bindingTemplates> </businessService> </businessServices> <categoryBag> <keyedReference tModelKey="uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2" keyName="Finance and Insurance" keyValue="52"/> </categoryBag> </businessEntity> </save_business> </soap-env:Body> </soap-env:Envelope>
In response to this request, the registry returns the businessDetail structure, which is the top-level element in the UDDI information model. It is used to represent information about an entity or business, as shown below:
<?xml version="1.0" encoding="UTF-8"?> <businessDetail generic="2.0" xmlns="urn:uddi-org:api_v2" operator="www.ibm.com/ services/uddi"> <businessEntity businessKey="25BF1920-D020-11D6-9314-000629DC0A7B" operator="www.ibm.com/services/uddi" authorizedName="100000CTU6"> <discoveryURLs> <discoveryURL useType="businessEntity"> http://uddi.ibm.com/testregistry/uddiget?businessKey=25BF1920-D020-11D6- 9314-000629DC0A7B </discoveryURL> </discoveryURLs> <name xml:lang="en">Flute Bank</name> <description xml:lang="en">A fictitious bank used for examples in the book Java Web Services Architecture, published by Morgan Kaufman, ISBN 1-55860-900-8. The authors can be reached at webservicesbook@yahoogroups.com OR http://www.javawebservicesarchitecture.com. </description> <contacts> <contact> <description xml:lang="en">The primary contact person for Flute Web services </description> <personName>John Malkovich</personName> <phone useType="">1-800-FLUTE-US</phone> <email>uddiadmin@flutebank.com</email> </contact> </contacts> <businessServices> <businessService serviceKey="25E12010-D020-11D6-9314-000629DC0A7B" businessKey="25BF1920- D020-11D6-9314-000629DC0A7B"> <name xml:lang="en">Billpayservice</name> <description xml:lang="en">A Web service allowing account holders to pay bills online</description> <bindingTemplates> <bindingTemplate bindingKey="2606F790-D020-11D6-9314-000629DC0A7B" serviceKey="25E12010-D020-11D6-9314-000629DC0A7B"> <description xml:lang="en">Flute Bank, beyond providing simple account-related services, also has the infrastructure to support online bill payments. The online bill payment system is offered to customers as a value-added service. </description> <accessPoint URLType="http">http://127.0.0.1:8080/billpayservice/jaxrpc/BillPay </accessPoint> <tModelInstanceDetails/> </bindingTemplate> </bindingTemplates> </businessService> </businessServices> <categoryBag> <keyedReference tModelKey="UUID:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2" keyName="Finance and Insurance" keyValue="52"/> </categoryBag> </businessEntity> </businessDetail>
Notice that in the businessDetail structure of the response, the registry has assigned the unique identifier to the entity and filled out the businessKey, operator, and authorizedName attributes. It has also assigned a unique discovery URL, where the XML describing the UDDI businessEntity structure for Flute Bank can be accessed.
All this communication and these SOAP messages are transparent to the Java application. The application deals only with the JAXR API and the information model objects contained therein; the provider takes care of the details.
-Dorg.apache.commons.logging.log=org.apache.commons.logging.impl.SimpleLog and -Dorg.apache.commons.logging.simplelog.defaultlog=debug.
Additionally, using -DuseSOAP=true enables JAXR to switch Soap4J instead of JAXM internally. This can be helpful when using JDK 1.4.
WSDL, with its abstract and concrete sections, was covered in Chapter 5; in Chapter 6, UDDI and how the WSDL elements map to UDDI elements was discussed. Let us now look at how JAXR can be used to publish service descriptions contained in the WSDL programmatically to a registry.
Let us look at the earlier example of the bill payment service. Suppose all the banks in the Good Banking Consortium got together and agreed to have a common bill payment service interface, so that customers of one bank could use the bill payment service of another bank to address a wider merchant account base. They could describe the billpayserviceinterface.wsdl shown in Listing 12.2a. (This is the same WSDL we covered in previous chapters.) Flute Bank could then implement this standard service and expose it as an endpoint. The WSDL is shown in Listing 12.2c. Notice that the schema definitions in billpayservice.wsdl have also been separated into their own XSD file, so that they can be reused across multiple service interfaces. Listing 12.2b shows the schema.
<?xml version="1.0" encoding="UTF-8"?> <definitions name="billpayservice-abstractinterface" targetNamespace="http:// www.flutebank.com/xml" xmlns:tns="http://www.flutebank.com/xml" xmlns="http:// schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap= "http://schemas.xmlsoap.org/wsdl/soap/"> <types> <schema targetNamespace="billpaydatatypes.xsd" xmlns:wsdl="http:// schemas.xmlsoap.org/wsdl/" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://www.w3.org/2001/XMLSchema"/> </types> <message name="BillPay_getLastPayment"> <part name="String_1" type="xsd:string"/> </message> <message name="BillPay_getLastPaymentResponse"> <part name="result" type="xsd:double"/> </message> <message name="BillPay_listScheduledPayments"/> <message name="BillPay_listScheduledPaymentsResponse"> <part name="result" type="tns:ArrayOfPaymentDetail"/> </message> <message name="BillPay_schedulePayment"> <part name="Date_1" type="xsd:dateTime"/> <part name="String_2" type="xsd:string"/> <part name="double_3" type="xsd:double"/> </message> <message name="BillPay_schedulePaymentResponse"> <part name="result" type="tns:PaymentConfirmation"/> </message> <message name="ScheduleFailedException"> <part name="ScheduleFailedException" type="tns:ScheduleFailedException"/> </message> <portType name="BillPay"> <operation name="getLastPayment" parameterOrder="String_1"> <input message="tns:BillPay_getLastPayment"/> <output message="tns:BillPay_getLastPaymentResponse"/> </operation> <operation name="listScheduledPayments" parameterOrder=""> <input message="tns:BillPay_listScheduledPayments"/> <output message="tns:BillPay_listScheduledPaymentsResponse"/> </operation> <operation name="schedulePayment" parameterOrder="Date_1 String_2 double_3"> <input message="tns:BillPay_schedulePayment"/> <output message="tns:BillPay_schedulePaymentResponse"/> <fault name="ScheduleFailedException" message="tns:ScheduleFailedException"/> </operation> </portType> <binding name="BillPayBinding" type="tns:BillPay"> <operation name="getLastPayment"> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/> </output> <soap:operation soapAction=""/> </operation> <operation name="listScheduledPayments"> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/> </output> <soap:operation soapAction=""/> </operation> <operation name="schedulePayment"> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/> </output> <fault name="ScheduleFailedException"> <soap:fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://www.flutebank.com/xml"/> </fault> <soap:operation soapAction=""/> </operation> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/> </binding> </definitions>
<?xml version="1.0" encoding="UTF-8"?> <schema targetNamespace="http://www.flutebank.com/xml" xmlns="http://www.w3.org/2001/ XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.flutebank.com/xml" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/> <complexType name="ArrayOfPaymentDetail"> <complexContent> <restriction base="soap-enc:Array"> <attribute ref="soap-enc:arrayType" wsdl:arrayType= "tns:PaymentDetail[]"/> </restriction> </complexContent> </complexType> <complexType name="PaymentDetail"> <sequence> <element name="date" type="dateTime"/> <element name="account" type="string"/> <element name="payeeName" type="string"/> <element name="amt" type="double"/> </sequence> </complexType> <complexType name="PaymentConfirmation"> <sequence> <element name="confirmationNum" type="int"/> <element name="payee" type="string"/> <element name="amt" type="double"/> </sequence> </complexType> <complexType name="ScheduleFailedException"> <sequence> <element name="message" type="string"/> <element name="localizedMessage" type="string"/> </sequence> </complexType> </schema>
<?xml version="1.0" encoding="UTF-8"?> <definitions name="billpayservice" targetNamespace="http://www.flutebank.com/ billpayservice" xmlns:tns="http://www.flutebank.com/xml" xmlns="http:// schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap= "http://schemas.xmlsoap.org/wsdl/soap/"> <import namespace="http://www.flutebank.com/xml" location="baillpayinterface.wsdl"/> <service name="Billpayservice"> <port name="BillPayPort" binding="tns:BillPayBinding"> <soap:address location="http://127.0.0.1:8080/billpayservice/jaxrpc/BillPay"/> </port> </service> </definitions>
In Chapter 5, we mentioned best practices relating to how the WSDL elements should be stored in the registry. The WSDL service interface should be represented as a tModel, so that it can be reused across service implementations. The service and port elements in the service implementation descriptions map to the businessService and bindingTemplate in the UDDI registry. Figure 12.17 shows the mapping of the WSDL elements to the UDDI structures for the BillPay Web service. Figure 12.18 shows the WSDL information as it would appear in the UDDI registry browser.
All the above mappings can be realized quite easily using JAXR. Let us look at how the WSDL can be published to a UDDI registry and examine the JAXR code for doing so. The JAXR client should:
Connect to the registry and authenticate with the username and password.
Locate the organization in the registry using the BusinessQueryManager. The organization should have been published previously, using either JAXR (previous example) or the provider's Web interface.
Publish the WSDL service interface of the Web service (i.e., the billpayserviceinterface.wsdl) as a tModel. In JAXR terms, this is a Concept with the namespace, description, and a link to the binding element specified in the service interface.
Classify the service interface as a WSDL specification in the UDDI registry. UDDI has defined the type wsdlSpec for this purpose.
Save the Concept and get a key for it. This will correspond to a key for the tModel in UDDI.
Publish the WSDL describing the service implementation (i.e., the billpayservice.wsdl) and reference the tModel used by the service. In JAXR terms, this means creating and saving a Service, the ServiceBinding, and a SpecificationLink object that links the bindings to the Concept in the above step.
Save the Service using the LifeCycleManager.
Note that in the ideal situation, steps 1 to 6 would be performed by the industry consortium (such as the Good Banking Consortium). Flute Bank would perform only step 6 and publish it under the service implementation within its own organization. In most cases today, however, the service interface and description would be published by the same business entity. The code in Listing 12.3 shows details about doing so with JAXR.
// imports not shown public class WSDLPublisher { private static final String QUERY_URL = "http://www-3.ibm.com:80/services/uddi/v2beta/inquiryapi"; private static final String PUBLISH_URL = "https://www-3.ibm.com:443/services/uddi/v2beta/protect/publishapi"; private static String uddiusername; private static String uddipassword; /** * Main method to publish the WSDL to the UDDI registry */ public static void main(String[] args) { if(args.length!=2){ System.out.println("Usage java UDDIPublish username uddipassword"); return; } uddiusername = args[0]; uddipassword = args[1]; try{ // Set the properties for the ConnectionFactory Properties environment = new Properties(); environment.setProperty("javax.xml.registry.queryManagerURL", QUERY_URL); environment.setProperty("javax.xml.registry.lifeCycleManagerURL", PUBLISH_URL); // Instantiate the factory and create a connection from it ConnectionFactory connfactory = ConnectionFactory.newInstance(); connfactory.setProperties(environment); Connection conn = connfactory.createConnection(); // Authenticate the username and password with the registry PasswordAuthentication passwdAuth = new PasswordAuthentication(uddiusername, uddipassword.toCharArray()); Set credentials = new HashSet(); credentials.add(passwdAuth); conn.setCredentials(credentials); // Obtain a reference to the RegistryService, the BusinessLifeCycleManager, // and the BusinessQueryManager RegistryService registryservice = conn.getRegistryService(); BusinessLifeCycleManager lifecyclemgr = registryservice.getBusinessLifeCycleManager(); BusinessQueryManager querymgr = registryservice.getBusinessQueryManager(); // First find the organization (this would already be registered from the // previous examples) Collection searchpattern = new ArrayList(); searchpattern.add("Flute Bank"); Collection findqualifier= new ArrayList(); findqualifier.add(FindQualifier.EXACT_NAME_MATCH); BulkResponse orgresponse = querymgr.findOrganizations(findqualifier, searchpattern, null, null, null, null); Collection orgs = orgresponse.getCollection(); Iterator orgiter= orgs.iterator(); // We don't need to iterate, because we know there is only one organization in the registry // called Flute Bank Organization fluteorg = (Organization) orgiter.next(); // Create a concept for the service interface of the WSDL Concept concept= lifecyclemgr.createConcept(null," http://www.flutebank.com/xml",null); InternationalString conceptdescription = lifecyclemgr.createInternationalString("The service interface of the bill payment Web service"); concept.setDescription(conceptdescription); // Note that the WSDL at this URL must be physically accessible. JAXR will access //the URL and ensure that the WSDL is valid ExternalLink link=lifecyclemgr.createExternalLink( "http://127.0.0.1:8080/billpayservice/ billpayserviceinterface.wsdl#BillPayBinding", "Wsdl service interface document"); concept.addExternalLink (link); // Classify the service interface as the WSDL Collection classification=new ArrayList(); ClassificationScheme uddiOrgTypes = querymgr.findClassificationSchemeByName(null, "uddi-org:types"); Classification wsdlclassification = lifecyclemgr.createClassification(uddiOrgTypes, "wsdlSpec", "wsdlSpec"); classification.add(wsdlclassification); concept.setClassifications(classification); // Save the concept (the UDDI registry will save this as a tModel and return a // Key to it) Collection concepts = new ArrayList(); concepts.add(concept); BulkResponse savedConcepts =lifecyclemgr.saveConcepts(concepts); Iterator conceptIterator =savedConcepts.getCollection().iterator(); if (conceptIterator.hasNext()){ javax.xml.registry.infomodel.Key key =(javax.xml.registry.infomodel.Key) conceptIterator.next(); concept.setKey(key); System.out.println("tModel key: "+ key.getId()); } // the service interface has been saved and the tModel created in the UDDI // registry // Create the concrete service (this maps to the businessService) Service service = lifecyclemgr.createService("Billpayservice"); InternationalString servicedescription = lifecyclemgr.createInternationalString("A Web service allowing account holders to pay bills online"); service.setDescription(servicedescription); // Create the service bindings for the Web service Collection serviceBindings = new ArrayList(); ServiceBinding binding = lifecyclemgr.createServiceBinding(); InternationalString bindingdescription = lifecyclemgr.createInternationalString("HTTP bindings for the Billpayservice Web service"); binding.setDescription(bindingdescription); // replace with the actual URL where the service is deployed binding.setAccessURI("http://127.0.0.1:8080/billpayservice/jaxrpc/BillPay"); // Create the specification link for the Web service SpecificationLink specLink=lifecyclemgr.createSpecificationLink(); // the concept now has the key created for the tModel specLink.setSpecificationObject(concept); binding.addSpecificationLink(specLink); serviceBindings.add(binding); // Add the service bindings to service service.addServiceBindings(serviceBindings); // Link the service to the provider service.setProvidingOrganization(fluteorg); // Add the Web service to the list of services, then add the list of services to // the organization Collection servicelist = new ArrayList(); servicelist.add(service); // Make the final call to the registry to save the services and get a response BulkResponse response = lifecyclemgr.saveServices(servicelist); System.out.println("services saved"); Collection exceptions = response.getExceptions(); // If there are no exceptions, the publish action was successful if (exceptions == null){ Collection keys = response.getCollection(); Iterator iterator = keys.iterator(); Key key = (Key) iterator.next(); String uid = key.getId(); System.out.println("The unique ID returned by the UDDI registry for the Organization is "+ uid); } // This means exceptions occurred during the publish action else { Iterator iterator = exceptions.iterator(); while (iterator.hasNext()) { Exception exception = (Exception) iterator.next(); System.out.println("Exception occurred while saving to the registry: " + exception); exception.printStackTrace(); } } // Finally, close the connection conn.close(); } catch (Exception exception) { System.out.println("General exception occurred: "+ exception); } } }
Although five usage combinations can be derived and are outlined below based on the concept of separating the service interface and service implementation, we believe that usage is currently typically centered around two use cases (items two and three).
Publishing the service interface only. This can be realized as the first part of Listing 12.3.
Publishing the service interface with one service implementation. This is Listing 12.3.
Publishing a complete WSDL document that contains both a service interface and service implementation interface. The realization for this would be identical to Listing 12.3.
Publishing a service implementation definition that uses multiple service interfaces. This would be the case if, for example, Flute Bank decided to implement two services, as shown below:
<import namespace="http://www.goodbankconsortium.com/xml" location="http://www.goodbankconsortium.com/creditcardinterface.wsdl"/> <import namespace="http://www.betterbankconsortium.com/xml" location="http://www.betterbankconsortium bankacountdebit.wsdl"/> <service name="Billpayservice"> <port name="CreditCardBillPayPort" binding="good:BillPayBinding"> <soap:address location="http://www.flutebank.com:8080/billpayservice/CardBillPay"/> </port> <port name="AccountBillPayPort" binding="better:BillPayBinding"> <soap:address location="http://www.flutebankx.com:8080/billpayservice/AccountBillPay"/> </port> </service>
The only difference between the realization for this and Listing 12.3 would be multiple ServiceBinding objects associated with the Service.
Publishing an abstract description that references another abstract description. In this case, instead of a Service, another Concept that refers would be created and published.
We have already covered the case of finding entities in the registry in the previous two examples, where we searched for an organization called Flute Bank and also for classifications.
Steps 1 through 8 remain the same as in the section Publishing Company Information to a UDDI Registry and in Listing 12.1, with one exception. Per the UDDI specifications, no authentication or SSL is needed to query the registry. Let us look at the subsequent steps:
Use the reference to the BusinessQueryManager and one of the find methods (see Figure 12.14) to query the registry. Listing 12.4 queries the registry for an organization whose name must exactly match "Flute Bank." This should return one or more organizations, one of which would be the organization we published in Listing 12.1.
Create a pattern to query on. For the above criteria, this would be FluteBank.
Create the qualifiers for the find operation.
Invoke the findOrganizations method on the BusinessQueryManager.
Parse the BulkResponse object returned from the find method and obtain the collection of Organization objects.
Query each Organization object to obtain details about the primary contact and services registered.
Close the Connection to the registry
Listing 12.4a shows the code; Listing 12.4b shows the corresponding output.
Listing 12.4a: Querying organization information from UDDI
import javax.xml.registry.infomodel.*; import javax.xml.registry.*; import java.util.*; public class UDDIQueryOrg { public static void main(String[] args) { // Set the properties for the ConnectionFactory Properties environment = new Properties(); environment.setProperty("javax.xml.registry.queryManagerURL", QUERY_URL); environment.setProperty("javax.xml.registry.lifeCycleManagerURL", PUBLISH_URL); // Instantiate the factory and create a connection from it ConnectionFactory connfactory = ConnectionFactory.newInstance(); connfactory.setProperties(environment); Connection conn = connfactory.createConnection(); Collection searchpattern = new ArrayList(); searchpattern.add("Flute Bank"); Collection findqualifier= new ArrayList(); findqualifier.add(FindQualifier.EXACT_NAME_MATCH); // Find using the name BulkResponse response = querymgr.findOrganizations(findqualifier, searchpattern, null, null, null, null); // Display information about the organizations found Collection orgs = response.getCollection(); Iterator orgiterator = orgs.iterator(); while (orgiterator.hasNext()) { Organization org = (Organization) orgiterator.next(); System.out.println("\t Organization name: " + org.getName().getValue()); System.out.println("\t Organization description: " + org.getDescription().getValue()); System.out.println("\t Organization uid: " + org.getKey().getId()); // Display information about the discovery URLs found Collection links = org.getExternalLinks(); Iterator linkiterator = links.iterator(); while(linkiterator.hasNext()){ ExternalLink link = (ExternalLink)linkiterator.next(); System.out.println("\t\t Link URI = " +link.getExternalURI()); } // Display information about the discovery URLs found Collection classify = org.getClassifications(); Iterator classifyiterator = classify.iterator(); while(linkiterator.hasNext()){ Classification clasf = (Classification)linkiterator.next(); System.out.println("\t\t Classification value = " +clasf.getValue()); } // Display primary contact information User pc = org.getPrimaryContact(); if (pc != null) { PersonName pcName = pc.getPersonName(); System.out.println("\t\t Primary contact name: " + pcName.getFullName()); Collection phNums = pc.getTelephoneNumbers(pc.getType()); Iterator phIter = phNums.iterator(); while (phIter.hasNext()) { TelephoneNumber num = (TelephoneNumber) phIter.next(); System.out.println("\t\t Phone number: " + num.getNumber()); } Collection eAddrs = pc.getEmailAddresses(); Iterator eaIter = eAddrs.iterator(); while (phIter.hasNext()) { System.out.println("\t\tEmail Address: " + (EmailAddress) eaIter.next()); } } } }
Listing 12.4b: Output of UDDIQuery
C:\jaxr\jwsa>java UDDIQueryOrg fluteadmin flutepassword Organization name: Flute Bank Organization description: A fictitious bank used for examples in the book Java Web Services Architecture, published by Morgan Kaufman, ISBN 1-55860-900-8. The authors can be reached at webservicesbook@yahoogroups.com OR www.javawebservicesarchitecture.com. Organization uid: 38920050-D028-11D6-9314-000629DC0A7B Link URI = http://uddi.ibm.com/testregistry/uddiget? businessKey=38920050-D028-11D6-9314-000629DC0A7B Primary contact name: John Malkovich Phone number: 1-800-FLUTE-US
We have looked at how service information can be published. Let us now look at how it can be retrieved from the registry by JAXR client applications. There are two broad use cases for retrieving the WSDL definition:
The client knows the organization and wants to retrieve one or more interfaces published by it.
The client knows the namespace corresponding to the service.
In the first case, the client must query the registry for a particular organization. It can retrieve this information based on name, classification, external identifiers, and other criteria. The organization can then be queried for the services published under it. Listing 12.5a shows how the WSDL published earlier can be retrieved using the Flute Bank organization name. Listing 12.5b shows its corresponding output.
import javax.xml.registry.infomodel.*; import javax.xml.registry.*; import java.util.*; public class UDDIQueryServices { private static final String QUERY_URL = "http://www-3.ibm.com:80/services/uddi/v2beta/inquiryapi"; /* Main method */ public static void main(String[] args) { try{ // Set the properties for the ConnectionFactory Properties environment = new Properties(); environment.setProperty("javax.xml.registry.queryManagerURL", QUERY_URL); // Instantiate the factory and create a connection from it ConnectionFactory connfactory = ConnectionFactory.newInstance(); connfactory.setProperties(environment); Connection conn = connfactory.createConnection(); // Obtain a reference to the RegistryService,the BusinessLifeCycleManager, // and the BusinessQueryManager RegistryService registryservice = conn.getRegistryService(); BusinessLifeCycleManager lifecyclemgr = registryservice.getBusinessLifeCycleManager(); BusinessQueryManager querymgr = registryservice.getBusinessQueryManager(); // prepare the arguments for the find operation Collection searchpattern = new ArrayList(); searchpattern.add("Flute Bank"); Collection findqualifier= new ArrayList(); findqualifier.add(FindQualifier.EXACT_NAME_MATCH); // Find using the name BulkResponse response = querymgr.findOrganizations(findqualifier, searchpattern, null, null, null, null); // Display information about the organizations found // In our case, only one should be returned Collection orgs = response.getCollection(); Iterator orgiterator = orgs.iterator(); while (orgiterator.hasNext()) { Organization org = (Organization) orgiterator.next(); System.out.println("Organization name: "+org.getName().getValue()); System.out.println("Organization uid: " + org.getKey().getId()); //Display service and binding information Collection services = org.getServices(); Iterator svcIter = services.iterator(); while (svcIter.hasNext()) { Service svc = (Service) svcIter.next(); System.out.println("\t\t Service name: " + svc.getName().getValue()); System.out.println("\t\t Service description: " + svc.getDescription().getValue()); Collection serviceBindings = svc.getServiceBindings(); Iterator sbIter = serviceBindings.iterator(); while (sbIter.hasNext()) { ServiceBinding sb =(ServiceBinding) sbIter.next(); System.out.println("\t\t\t Binding "+ "Description: " +sb.getDescription().getValue()); System.out.println("\t\t\t Access URI: " + sb.getAccessURI()); Collection servicespecs= sb.getSpecificationLinks(); Iterator servicespecit=servicespecs.iterator(); while(servicespecit.hasNext()){ SpecificationLink spec= (SpecificationLink)servicespecit.next(); Concept tModel= (Concept)spec.getSpecificationObject(); // get the tModel System.out.println("\t\t\t\t Service Interface :" +tModel.getDescription().getValue()); Iterator extlinks=tModel.getExternalLinks().iterator(); while(extlinks.hasNext()){ ExternalLink extlink=(ExternalLink)extlinks.next(); System.out.println("\t\t\t\t\t Service Interface location : "+extlink.getExternalURI()); System.out.println("\t\t\t\t\t Location description:" +extlink.getDescription().getValue()); } } } } } // Finally, close the connection conn.close(); } catch (Exception exception) { System.out.println("General exception occurred: " + exception); } } }
C:\jaxr\jwsa>java UDDIQueryServices flutebank fluteadmin Organization name: Flute Bank Organization uid: 46F5D8A0-D3D5-11D6-8370-000629DC0A7B Service name: Billpayservice Service description: A Web service allowing account holders to pay bills online Binding Description: HTTP bindings for the Billpayservice Web service Access URI: http://127.0.0.1:8080/billpayservice/jaxrpc/BillPay Service Interface: The service interface of the bill payment Web service Service Interface location: http://127.0.0.1:8080/billpayservice/billpayserviceinterface.wsdl#BillPayBinding Location description: Wsdl service interface document
In the second case, the client can query the registry based on the namespace declaration from the WSDL defining the service interface. The namespace corresponds to the name of the concept (i.e., the tModel name) classified with the wsdlSpec in UDDI, as shown earlier. This may be helpful in two cases:
The parties can agree upon the namespace as part of the service level agreement between them.
The client may want to query the registry to find service implementation based on service interfaces defined by industry verticals. For example, we mentioned earlier how the Good Banking Consortium may define the BillPay service implemented by Flute. The namespace of this service interface will be that as defined by the consortium.
Listing 12.6a shows how the service can be queried on namespace. Listing 12.6b shows the corresponding output.
import javax.xml.registry.*; import javax.xml.registry.infomodel.*; import java.util.*; public class UDDIQueryServicesByNamespace { private static final String QUERY_URL="http://uddi.microsoft.com:80/inquire"; /* Main method of the class*/ public static void main(String[] args) { try{ // Set the properties for the ConnectionFactory Properties environment = new Properties(); environment.setProperty("javax.xml.registry.queryManagerURL", QUERY_URL); // Instantiate the factory and create a connection from it ConnectionFactory connfactory = ConnectionFactory.newInstance(); connfactory.setProperties(environment); Connection conn = connfactory.createConnection(); // Obtain a reference to the RegistryService,the BusinessLifeCycleManager // and the BusinessQueryManager RegistryService registryservice = conn.getRegistryService(); BusinessLifeCycleManager lifecyclemgr = registryservice.getBusinessLifeCycleManager(); BusinessQueryManager querymgr = registryservice.getBusinessQueryManager(); // prepare the parameters for the find operation Collection findqualifier= new ArrayList(); findqualifier.add(FindQualifier.EXACT_NAME_MATCH); // WSDL tModels must be classified under the wsdlSpec classification in UDDI Collection classifications = new ArrayList(); ClassificationScheme uddiOrgTypes = querymgr.findClassificationSchemeByName(null, "uddi-org:types"); Classification wsdlSpecClassification = lifecyclemgr.createClassification(uddiOrgTypes, "wsdlSpec", "wsdlSpec"); classifications.add(wsdlSpecClassification); // WSDLs corresponding to this namespace Collection searchpattern = new ArrayList(); searchpattern.add("%http://www.flutebank.com/xml%"); // find the Concepts (i.e., the tModels) BulkResponse response = querymgr.findConcepts(null, searchpattern, classifications, null, null); Collection specConcepts = response.getCollection(); Iterator iter = specConcepts.iterator(); while (iter.hasNext()) { try { Concept concept = (Concept)iter.next(); String name = concept.getName().getValue(); Collection extlinks = concept.getExternalLinks(); System.out.println("WSDL :\n\t Namespace: " + name + "\n\t Key: " + concept.getKey().getId() + "\n\t Description: " + concept.getDescription().getValue()); Iterator linkiter=extlinks.iterator(); while(linkiter.hasNext()) { ExternalLink link = (ExternalLink)linkiter.next(); System.out.println("\t WSDL location : " + link.getExternalURI()); } // Find all the organizations using this WSDL definition Collection tmodels = new ArrayList(); tmodels.add(concept); response = querymgr.findOrganizations(null, null, null, tmodels, null, null); Collection orgs = response.getCollection(); Iterator orgIter = orgs.iterator(); if (orgIter.hasNext()) System.out.println("Organizations using the " + name + " WSDL namespace:"); else System.out.println("No Organizations using the WSDL " + name); while (orgIter.hasNext()) { Organization org = (Organization)orgIter.next(); System.out.println("\t Name: " + org.getName().getValue() + "\n\t Key: " + org.getKey().getId() + "\n\t Description: " + org.getDescription().getValue()); } }catch (JAXRException e) { e.printStackTrace(); } } } catch (JAXRException e) { e.printStackTrace(); } } }
C:\jaxr\jwsa>java UDDIQueryServicesByNamespace WSDL : Namespace: http://www.flutebank.com/xml Key: UUID:67191F10-D3D6-11D6-8370-000629DC0A7B Description: The service interface of the bill payment Web service WSDL location: http://127.0.0.1:8080/billpayservice/billpayserviceinterface.wsdl#BillPayBinding Organizations with service implementations for namespace www.flutebank.com/xml Name: Flute Bank Key: 46F5D8A0-D3D5-11D6-8370-000629DC0A7B Description: A fictitious bank used for examples in the book Java Web Services Architecture, published by Morgan Kaufman, ISBN 1-55860-900-8. The authors can be reached at webservicesbook@yahoogroups.com OR http://www.javawebservicesarchitecture.com.
We have just looked at how the service information can be published and retrieved from the UDDI registry. In Chapter 2, we talked about the register-find-bind scenario for Web services. In most cases, service consumers will locate the WSDL and generate the client-side code in Java or other languages for consuming that service.
In Chapter 9, we looked at how to do this for both Java and C#. This is a typical use of the static compile-time binding pattern discussed in Chapter 5. In most cases, clients will not look up the UDDI registry at runtime for this information, because the WSDL published in the registry will be part of some service level or business agreement and will not be expected to change often.
We also discussed the second static deploy-time binding pattern, where the portType is known but the location is retrieved at runtime. Let us look at a strategy for implementing this pattern:
Design-time tools can discover the service in the UDDI registry and retrieve its service interface and the service implementation. Though we looked at how to do this programmatically with JAXR, we envision that vendor-provided tools will perform this task at design time, using APIs such as those provided by JSR-110 (Java API for WSDL) in combination with JAXR.
Either way, the client application will have complete service information described by the JAXR Service and ServiceBinding (containing the key), Concept, and ExternalLink. It can then store the ServiceBinding key, which corresponds to the UDDI bindingKey in some configuration variable.
When the client is executed, it can use this stored ServiceBinding key to retrieve the ServiceBinding information from the BusinessQueryManager. For example:
BulkResponse response=querymgr.findServiceBindings(serviceKey,null,null,null);
We have already discussed this static deploy-time pattern earlier but clearly this can be useful if:
The service location changes.
The client is created and distributed by the service provider to different service consumers.
The service invocation fails, at which time the client can query the registry for a mirror location for the same service defined by the service provider.
Though we have discussed how information can be published progammatically to the registry, it is quite common for an enterprise to follow an administrator-driven approach during application deployment into production environments Here, for example, a designated person is responsible for publishing information in the UDDI registry using the browser-based interface. This also makes sense, because information such as company address, contact information, and service location is expected to remain static and is included as part of the service level agreements forged with business partners.