Einführung in UDDI und JAXR

Wo ist mein Web Service?

Dapeng Wang

UDDI (Universal Description Discovery and Integration) stellt neben SOAP und WSDL die dritte Technologiesäule im Umfeld von Web Services dar. Über UDDI können Web Services publiziert und lokalisiert werden. Um die Zugriffe auf UDDI innerhalb von Java zu vereinfachen, wurde JAXR (Java API for XML Registry) als Standard-API im Rahmen von JCP eingeführt, das gleichzeitig eine Kernkomponente im JAX-Paket darstellt. In diesem Artikel werden die Basisdatentypen und die Funktionsweise von UDDI erläutertet, um dann die Architektur und die wichtigen Konzepte in JAXR vorzustellen. Abschließend wird anhand von Codebeispielen gezeigt, wie man mit JAXR Web Services in UDDI-Registry publizieren bzw. lokalisieren kann.

Serviceorientierte Architektur

Seit geraumer Zeit ist das Schlagwort Serviceorientierung bzw. serviceorientierte Architektur voll im Trend. Die Idee hinter Serviceorientierung ist recht einfach: Statt fein strukturierte Objekte, die oft Abhängigkeit von anderen Objekten besitzen und somit allein schlecht wiederverwendbar sind, werden in einem serviceorientierten Paradigma Services, die wesentlich grobgranularer sind, als Einheit identifiziert. Diese Einheit kapselt die interne Struktur und bietet nach außen eine klar definierte Schnittstelle, die die Funktionalität dieses Services beschreibt. Außerdem müssen Services nicht als Softwarekomponenten verteilt werden, was zusätzlich Deployment- und Wartungsprobleme hervorrufen würde, sondern zentral bereitgestellt werden und können über das Netzwerk angesprochen werden. Damit ein Service auch möglichst von vielen Clients aufgerufen werden kann, sollte er ein neutrales, plattform- und programmiersprachenunabhängiges Protokoll unterstützen. Ein weiteres Problem, das mit der serviceorientierten Architektur (SOA) gelöst werden soll, ist das Publizieren und Auffinden von wiederverwendbaren Services. In der Praxis kommt es häufig vor, dass dieselbe Funktionalität mehrmals von verschiedenen Abteilungen in einem Unternehmen implementiert wird, weil nicht bekannt ist, dass die benötigte Komponente schon vorhanden ist. SOA löst dieses Problem, indem alle wiederverwendbaren Services in einem zentralen Verzeichnis oder einer Registry registriert werden. Diese zentrale Registry verwaltet die Metainformationen zu den Services in einem Repository und stellt diese Information über eine definierte Schnittstelle für Clients zur Verfügung. In einer serviceorientierten Architektur werden drei Rollen identifiziert (Abb.1):

Abb. 1: Das magische Dreieck

Web Services sind typischerweise auf der serviceorientierten Architektur aufgebaut. Die Rolle des Service Brokers wird von XML-Verzeichnissen wie UDDI oder ebXML-Registry in Web Services gespielt.

UDDI

Durch die Verbreitung der Web Service-Technologie und den zunehmenden Bedarf an B2B-Kollaborationen im Web wurde schnell erkannt, dass ein Vermittlungsmechanismus fehlt, der die Verbindungen zwischen den Geschäftspartnern im Web herstellen bzw. vermitteln kann. Dafür wurde von IBM, Microsoft und Ariba UDDI ins Leben gerufen, was heutzutage den De-Facto-Standard für XML-Verzeichnisse von Web Services darstellt. Wenn man das Architekturpicture einer typischen verteilten Anwendung (sei es auf CORBA-, RMI- oder EJB-Basis) mit dem der serviceorientierten Architektur vergleicht, wird sofort ersichtlich, dass UDDI bzw. der Service Broker die Rolle des Namensdiensts (CoSNaming, RMIRegistry oder JNDI) in klassischen verteilten Architekturen spielt, der besonders in der Bootstrapping-Phase wichtig ist. UDDI bietet aber wesentlich mehr Funktionalitäten als ein bloßer Namensdienst wie JNDI, weil die Daten in der UDDI-Registry auf verschiedene Arten abgefragt werden können:

  • White Pages: Die UDDI-Registry enthält Namen, Beschreibung und sämtliche Kontaktinformationen von Unternehmen, die Web Services publiziert haben.
  • Yellow Pages: Alle Unternehmen sowie ihre veröffentlichten Services werden innerhalb der UDDI-Registry klassifiziert und kategorisiert, sodass die ganze Registry wie ein Branchenbuch oder die Gelben Seiten benutzt werden kann.
  • Green Pages: UDDI-Registry enthält zusätzlich noch die technischen Dokumentationen, die die veröffentlichten Services beschreiben.

Die wesentlichen Bestandteile der UDDI-Spezifikation sind die Beschreibung der Datenstruktur in UDDI und das API zum Abfragen und Publizieren von Services in der UDDI-Registry. UDDI ist selbst eine Web Service-Anwendung und benutzt SOAP als Kommunikationsprotokoll. Alle APIs und Datenstrukturen werden daher in XML- bzw. XML Schema-Format spezifiziert. Das Datenmodell von UDDI kennt im Wesentlichen die folgenden fünf Datenstrukturen, die auch in Abpictureung 2 dargestellt sind:

Abb. 2: Datenstrukturen in UDDI

Ein tModel ist an sich eine einfache Datenstruktur. Jedes tModel wird beim Anlegen einer UUID zugeordnet und besitzt außerdem Subelemente wie Name, Beschreibung oder Übersichtsdokument. Während zwischen businessEntity und businessService sowie zwischen businessService und bindingTemplate eine Eltern-Kinder-Beziehung besteht, werden tModels nur von anderen Datenstrukturen referenziert und sind nicht als Kind-Elemente in anderen Datenstrukturen enthalten. Der Grund liegt darin, dass jedes tModel ein eigenständiges Konzept darstellt, das keiner anderen Datenstruktur untergeordnet ist. Man kann tModels anhand einer Tabelle in einer Datenbank veranschaulichen, die folgende Spalten besitzt: ID, Name, Beschreibung, URL (Tabelle 1). Wichtig ist, dass diese Tabelle eigenständig ist und somit ohne andere Tabelle existieren kann. Diese Tabelle fungiert als eine Mappings-Tabelle, die auch als Domänentable oder Katalogtable bekannt ist.

ID Name Beschreibung URL
1 Telefon Repräsentiert eine Telefonnummer www.telephone.org/
2 Fax Repräsentiert eine Faxnummer www.fax.org/
3 Steuernummer Repräsentiert eine Steuernummer www.steuer.org/
4 Aktienkursservice Repräsentiert einen Service, der Aktienkurs liefert www.quote.com/quote.wsdl

Ein tModel wird innerhalb UDDI für zwei verschiedene Zwecke verwendet. Zum einen kann ein tModel einen technischen Fingerprint oder eine technische Spezifikation beschreiben. Ein typisches Beispiel für diese Art der Verwendung ist das Ablegen eines WSDL-Dokuments als Servicespezifikation in die UDDI-Registry, wie das tModel mit der ID 4 in der Tabelle 1. Ein solches WSDL-Dokument enthält die Beschreibung der abstrakten Serviceschnittstelle mit Datentypen (type), Nachrichten (message) und Porttyp (port type & operation). Diese abstrakte Servicespezifikation repräsentiert einen eigenen Servicetyp und wird daher in UDDI unter einem tModel mit einer eindeutigen UUID abgelegt. Wenn ein Service diese abstrakte Servicespezifikation bzw. diesen Servicetyp unterstützt, kann er dies ausdrücken, indem er in seinen tModelInstanceDetails auf die UUID dieses tModels referenziert. Ein Service Consumer, der diesen Service nutzen will, kann dann das tModel bzw. den Servicetyp studieren, um diesen Service korrekt aufzurufen.
Ein tModel kann ebenfalls verwendet werden, um abstrakte Namensräume für Typisierungen zu definieren. Wenn die drei folgenden Nummern ohne Kontext betrachtet werden, ist es schwer zu erkennen, was diese Nummern genau bedeuten:

  • 00496221881588
  • 00496221881599
  • 9526878988

Wenn aber jeder Nummer ein tModel oder der ID eines tModels zugeordnet wird, dann ist die Semantik der einzelnen Nummern sofort klar: Für die erste Nummer wird tModelKey=1 zugeordnet. D.h., es handelt sich bei 00496221881588 um eine Telefonnummer. Die zweite Nummer (die dritte) bekommt tModelKey=2 (tModelKey=3) und 00496221881599 (9526878988) ist daher eine Faxnummer (Steuernummer).

In diesem Fall definiert jedes tModel einen eigenen Namensraum, der für die Klassifizierung bzw. Kategorisierung von Datenstrukturen in UDDI verwendet werden kann. Datenstrukturen wie businessEntity oder businessService können optional zwei Subelemente besitzen: identifierBag und categoryBag. Ein identifierBag ist nichts anderes als eine Sammlung von Key-Value-Paaren, die nur in einem bestimmten Kontext sinnvoll sind. Dieser Kontext entspricht dem Namensraum, der durch das zugehörige tModel identifiziert wird. Ähnlich ist categoryBag aufgebaut, nur dass der vom entsprechenden tModel identifizierte Namensraum auf eine vordefinierte Kategorie (Taxonomy) verwiesen wird.
In UDDI sind Standard-Identifiertypen und -Kategorien definiert, die in Tabellen 2 und 3 aufgelistet sind.

Taxonomy tModel Beschreibung
NAICS ntis-gov:naics:1997 North American Industry Classification System
UNSPSC unspsc-org:unspsc Universal Standard Products and Services Classification
ISO 3166 uddi-org:iso-ch:3166:1999 IDO 3166, internationaler Standard für geographische Regionen
Andere Taxonomy uddi-org:general_keywords Klassifizierung nach Schlüsselwort

Als Abschluss zum Thema Datenstruktur in UDDI wird beispielhaft eine businessEntity in Listing 1 dargestellt, in der die verschiedenen Datenstrukturen in UDDI zu sehen sind.

Listing: 1 UDDI-Datenstruktur

http://localhost:8080/registry-server/businessEntity?businessKey=f6058fd2-47f6-058f-e5e8-f3b5bf62f171Java StarSoftware training and consultingDapeng Wang0049 172 9526988Wang.Dapeng@gmx.netFormat Java CodeThis service helps you to format Java source code.Servce binding description.http://localhost:8080/

Die APIs in UDDI sind in Form von XML-Segmenten spezifiziert, die beim Aufruf in SOAP-Envelope eingebettet werden. Generell lassen sich die Requests in zwei Gruppen unterteilen: Inquiry-API und Publisher-API. Das Inquiry-API enthält alle Requests zur Suche nach einem bestimmten UDDI-Datentyp (find_business, find_service, find_binding und find_tModel) sowie die Requests zur Abfrage von Details der Datentypen (get_businessDetail, get_serviceDetail, get_bindingDetail und get_tModelDetail usw.). Diese Methoden werden in erster Linie von Service Consumer in Anspruch genommen und verlangen keine zusätzliche Authentifizierung. Das Publisher-API enthält dagegen Requests zum Verändern bzw. Anlegen von Daten (save_business, save_service, save_binding und save_tModel) sowie Requests zum Löschen von Daten (delete_business, delete_service, delete_binding und delete_tModel). Diese Requests verlangen einen Sicherheits-Token, den der Benutzer beim Anmelden mit dem Request get_authToken erlangen und beim Abmelden mit dem Request discard_authToken verwerfen kann.

Zur Veranschaulichung wird in Listings 2 und 3 das Request-Response-Paar eines UDDI-Aufrufs gezeigt. Hier wird als Suchkriterium ein Name-Value-Paar in dem Namensraum angegeben, der durch das tModel mit uuid:4e49a8d6-d5a2-4fc2-93a0-0411d8d19e88 definiert ist. Da hinter dieser UUID das tModel ISO 3166 für geographische Regionen steht, bedeutet es einfach, dass hier nach Unternehmen gesucht wird, die in Deutschland ansässig sind.

Listing: 2 UDDI Inquiry Request für find_business

Listing: 3 UDDI Inquiry Response für find_business

Java StarSoftware training and consultingFormat Java Code

Es ist zwar möglich, einen UDDI-Request im geeigneten Format wie in Listing 2 zu erstellen und diesen an die UDDI-Registry zu schicken. Die manuelle Erstellung von SOAP-Dokument ist jedoch mühsam und fehleranfällig. Das SAAJ-API kann Hilfestellung leisten, indem es den Aufbau der SOAP-Nachricht vereinfacht. Aber auch hier muss man direkt mit XML arbeiten, um die einzubettenden UDDI-Segmente zu erstellen. Abhilfe schafft das JAXR-API, das eine Kommunikation mit UDDI erlaubt, ohne dabei direkt XML schreiben zu müssen.

JAXR ist aus JSR 93 entstanden und stellt ein high-level API zur Verfügung, das die internen Nachrichten für die Kommunikation mit XML-Registries generiert und an XML-Registries verschickt. Ein Ziel von JAXR ist es, Kommunikation mit verschiedenen XML-Registries zu unterstützen. Aktuell wird neben UDDI auch die ebXML-Registry unterstützt, das im Vergleich mit UDDI noch komplexer und umfangreicher ist. Es handelt sich bei JAXR um eine Spezifikation, die nur ein abstraktes API festlegt, das dann von verschiedenen Providern implementiert werden kann.
In der JAXR-Architektur, die in Abpictureung 3 gezeichnet ist, sind die in JAXR definierten Rollen deutlich zu erkennen:

Abb. 3: JAXR-Architektur

Da zwischen den Registries durchaus Unterschiede an Datentypen und Funktionalitäten existieren und diese Unterscheide nicht immer durch ein allgemeines API abstrahiert werden können, ist ein Konzept namens Capability-Profile in JAXR eingeführt worden. Jede Methode in der JAXR-API ist mit einem Capability-Level vorgesehen und die Methoden mit den selben Levels pictureen zusammen ein Capability-Profile. Im Moment sind zwei Capability-Profiles definiert: Das Capability-Profile mit Level 0 für Grundfunktionalitäten und das mit Level 1 für fortgeschrittene Funktionalitäten. Jeder JAXR-Provider muss mindestens das Capability-Profile Level 0 unterstützen. Ebenfalls müssen JAXR-Provider UDDI Level 0- und ebXML Level 1-kompatibel sein. Mit der Methode RegistryService.getRegistryService().getCapabilityProfile() kann das Capability-Profile des aktuellen JAXR-Providers ermittelt werden. Wird eine Methode aufgerufen, die ein höheres Capability-Level besitzt als das von JAXR-Provider, wird eine UnsupportedCapabilityException geworfen.

Während sich die wichtigsten JAXR-Klassen im Package javax.xml.registry befinden, liegen alle Datenstrukturen in JAXR im Subpackage javax.xml.registry.infomodel. Für die Infomodel-Klassen hat JAXR das XML Registry Information Model 2.0 als Grundlage gewählt, sodass die Bezeichnungen in JAXR von den UDDI-Terminologien abweichen. Eine Abpictureung zwischen JAXR- Infomodel-Klassen und UDDI-Datenstrukturen ist in Tabelle 4 dargestellt.

UDDI-Datenstruktur JAXR-Interface
businessEntity Organization
businessService Service
bindingTemplate ServiceBinding
tModel (Fingerprint) Concept
tModel (Namensraum) ClassificationScheme
discoveryURL ExternalLink
contact User
identifierBag Collection von ExternalIdentifier
categoryBag Collection von Classfication
address PostalAddress
overviewDoc ExternalLink

Alle Infomodel-Klassen sind vom Interface RegistryObject abgeleitet, in dem die gemeinsamen Attribute und Basisfunktionalitäten für die Verwaltung von Identifiern und Kategorien deklariert sind. Ansonsten ist zu erwähnen, dass für Texte in JAXR statt ein einfacher String immer das Interface InternationalString verwendet wird, das den Textinhalt in mehreren Locals verwalten kann.

Bei der Entwicklung von JAXR-Applikationen wird immer das gleiche Programmiermodell verfolgt. Zuerst wird eine Verbindung zum JAXR-Provider mit der Hilfe von ConnectionFactory hergestellt. Mit der Property javax.xml.registry.ConenctionFactoryClass kann gesteuert werden, welche Factory-Klasse für die Erzeugung von Connections verwendet werden soll. Doch bevor eine Connection-Instance erzeugt wird, muss noch diese Factory mit entsprechenden Properties konfiguriert werden. Dabei sollen die beiden Properties javax.xml.registry.queryManagerURL und javax.xml.registry.lifeCycleManagerURL auf jeden Fall gesetzt sein, damit der JAXR-Provider die Registry-Adresse kennt. Eine Verbindung kann dann mit factory.createConnection() aufgebaut werden (Listing 4).

Listing: 4 Verbindung zu JAXR-Provider herstellen

......

public static final String queryURL = "http://localhost:8080/RegistryServer";
public static final String lifeCycleURL = http://localhost:8080/RegistryServer";

......
Properties prop = new Properties();
prop.setProperty("javax.xml.registry.queryManagerURL", queryURL);
prop.setProperty("javax.xml.registry.lifeCycleManagerURL ", lifeCycleURL);
ConnectionFactory factory = ConnectionFactory.newInstance();
factory.setProperties(prop);
conn = factory.createConnection();
System.out.println("Connection created to registry.");

Als nächstes muss das RegistryService-Objekt von der Connection geholt werden, weil es Referenzen zum BusinessQueryManager (bzw. DeclarativeQueryManager) und BusinessLifeCycleManager liefert. Außerdem kann über RegistryService-Objekt Information über das von diesem JAXR-Provider unterstützte Capability-Profile ermittelt werden (Listing 5).

Listing: 5 Referenz zu BusinessLifeCycleManager bzw. BusinessLifeCycleManager holen

RegistryService rs = conn.getRegistryService();
CapabilityProfile cp = rs.getCapabilityProfile();
System.out.println("CapabilityLevel. " + cp.getCapabilityLevel());

BusinessQueryManager queryManager = rs.getBusinessQueryManager();
System.out.println("Got BusinessQueryManager");
BusinessLifeCycleManager lifeCycleManager = rs.getBusinessLifeCycleManager();
System.out.println("Got BusinessLifeCycleManager ");

Nach der Initialisierung der Infomodel-Objekte können Requests zum Abfragen oder Publizieren über die Manager abgesetzt werden. Das Ergebnis der Aufrufe wird immer in Form von BulkReponse präsentiert, die mehrere JAXRReponse enthält. Zum Schluss soll die Verbindung zum Provider geschlossen werden.
Zuerst wird in Listing 6 demonstriert, wie man eine Organisation mit JAXR in UDDI registriert.

Listing: 6 Registrieren einer Organisation in Registry

public void doPublish() throws JAXRException {
Organization org = blm.createOrganization("Java Star");
InternationalString description = blm.createInternationalString("Software training and consulting");
org.setDescription(description);

User user = blm.createUser();
user.setPersonName(blm.createPersonName("Dapeng Wang"));
TelephoneNumber tel = blm.createTelephoneNumber();
tel.setNumber("0049 172 9526988");
Collection tels = new ArrayList();
tels.add(tel);
user.setTelephoneNumbers(tels);
EmailAddress email = blm.createEmailAddress("Wang.Dapeng@gmx.net");
Collection emails = new ArrayList();
emails.add(email);
user.setEmailAddresses(emails);

org.setPrimaryContact(user);

ClassificationScheme scheme = bqm.findClassificationSchemeByName(null, "uddi-org:iso-ch:3166:1999");
Classification classification = blm.createClassification(scheme, "Germany", "DE");
Collection classifications = new ArrayList();
classifications.add(classification);
scheme = bqm.findClassificationSchemeByName(null, "ntis-gov:naics:1997");
classification = blm.createClassification(scheme, "Computer Systems Design Services", "541512");
classifications.add(classification);
org.addClassifications(classifications);

Service service = blm.createService("Format Java Code");
description = blm.createInternationalString("This service helps you to format Java source code.");
service.setDescription(description);
ServiceBinding binding = blm.createServiceBinding();
description = blm.createInternationalString("Servce binding description.");
binding.setDescription(description);
binding.setAccessURI("http://localhost:8080/");
service.addServiceBinding(binding);
org.addService(service);

Collection orgs = new ArrayList();
orgs.add(org);

BulkResponse response = blm.saveOrganizations(orgs);
Collection exceptions = response.getExceptions();
if(exceptions==null) {
System.out.println("Organizsation saved successfully.");
Collection keys = response.getCollection();
Iterator iter = keys.iterator();
while (iter.hasNext()) {
Key key = (Key)iter.next();
System.out.println("Organization key: " + key.getId());
}
}else {
System.out.println("Error while saving organozation.");
Iterator iter = exceptions.iterator();
while (iter.hasNext()) {
Exception ex = (Exception)iter.next();
System.out.println("Exceptin occured: " + ex);
}
}
}

In der doPublish-Methode wird ein Unternehmen namens Java Star registriert, das auch einen Service Format Java Code publiziert. Zusätzlich wird dieses Unternehmen mit zwei Kategorien versehen, die besagen, dass das Unternehmen seinen Sitz in Deutschland hat (ISO 3166-Kategorie) und im Bereich Computer Systems Design Services tätig ist (NAICS-Kategorie). Der Code kann gegen die Test-Registry ausgeführt werden, die ein Bestandteil von JWSDP (Java Web Service Developer Pack) ist. In diesem Paket ist auch ein Registry-Browser vorhanden, der verschiedene JAXR-Funktionalitäten über eine Swing-Oberfläche bereitstellt. Das Ergebnis in Listing 6 kann mit diesem Registry-Browser kontrolliert werden.

Abb. 4: Registry-Browser

Um die in Listing 6 registrierte Organisation wiederzufinden, kann das Inquiry-API in JAXR benutzt werden (Listing 7).

Listing: 7 Suchen einer Organisation in Registry anhand Kategorie

public void doCategoryQuery() throws JAXRException {
ArrayList findQualifierList = new ArrayList();
findQualifierList.add(FindQualifier.SORT_BY_NAME_ASC);

Collection classifications = new ArrayList();
ClassificationScheme scheme =
bqm.findClassificationSchemeByName(null, "ntis-gov:naics:1997");
Classification classification = blm.createClassification(scheme, "Computer Systems Design Services", "541512");
classifications.add(classification);

BulkResponse response = bqm.findOrganizations(findQualifierList, null, classifications, null, null, null);

if (response.getStatus() == JAXRResponse.STATUS_SUCCESS) {
System.out.println("Query successful.");
Collection orgs = response.getCollection();
System.out.println("Organizations found: " + orgs.size());
Iterator iter = orgs.iterator();
while (iter.hasNext()) {
Organization org = (Organization) iter.next();
System.out.println("Organization Name: " +
org.getName().getValue());
System.out.println("Organization Key: " + org.getKey().getId());
System.out.println("Organization Description: " +
org.getDescription().getValue());

Collection services = org.getServices();
Iterator siter = services.iterator();
while (siter.hasNext()) {
Service service = (Service) siter.next();
System.out.println("tService Name: " +
service.getName().getValue());
}

classifications = org.getClassifications();
Iterator citer = classifications.iterator();
while (citer.hasNext()) {
classification = (Classification) citer.next();
System.out.println("tClassification Description Key: " +
classification.getClassificationScheme().
getDescription().getValue());
System.out.println("tClassification Name: " +
classification.getName().getValue());
}

Collection identifiers = org.getExternalIdentifiers();
Iterator iiter = identifiers.iterator();
while (iiter.hasNext()) {
ExternalIdentifier identifier = (ExternalIdentifier) iiter.
next();
System.out.println("tExternalIdentifier Name: " +
identifier.getName().getValue());
}
}
}
}

Es ist zwar in der serviceorientierten Architektur nicht zwingend notwendig, dass ein Service Broker wie UDDI-Registry benutzt wird, doch stellt UDDI eine sehr nützliche Technologie dar, die das Lokalisieren von wiederverwendbaren Services wesentlich erleichtert. Dabei muss man die Nutzung nicht nur auf die globale Verzeichnisse, die von den Big Players wie IBM, Microsoft oder SAP betrieben werden, beschränken. Es ist zu erwarten, dass immer mehr UDDI-Verzeichnisse innerhalb eines Unternehmen eingerichtet werden, wo die wiederverwendbaren Services registriert sind, um die Wiederverwendung von Komponenten in einem Unternehmen zu fördern. JAXR komplettiert dieses technologische Umfeld und kann mit seinem high-level API die Zugriffe auf UDDI-Registry enorm vereinfachen.

Links und Literatur

Geschrieben von
Dapeng Wang
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.