Abraxas
This is version . It is not the current version, and thus it cannot be edited.
Back to current version   Restore this version

Intersult Abraxas ist ein Werkzeug für die automatisierte Kommunikation im Netzwerk. Anwender sind alle, die auf Services im eigenen oder anderen Unternehmen zugreifen wollen.

Technologisch handelt es sich um das Einlesen und Speichern im XML-Format, die Kommunikation über SOAP, REST, sowie das Generieren von Java-Klassen, Serialisieren von Java-Objekten in XML und umgekehrt.

Bugs und Feature-Requests können über https://code.google.com/p/abraxas-maven/issues/list eingegeben werden.

Ein Beispiel-Projekt mit Maven ist als ZIP-Datei Abraxas/abraxas-example.zip(info) verfügbar.

Inhalt#

Bestandteile#

Abraxas besteht im Wesentlichen aus folgenden Bestandteilen:

Was macht Abraxas?#

ist das Produkt der Intersult für den Umgang mit Remote-Verbindungen und -Services im Bereich XML und Java. Abraxas ist ein jahrelang entwickeltes und erprobtes Produkt, auf das mehrere renommierte Unternehmen setzen, weil es eines der führenden XML- und SOAP-Werkzeuge darstellt.

Das XML-Paket kann unter Maven 2/3 direkt aus dem Intersult Maven Repository bezogen werden. Für Rechte und Einsatz in kommerziellen Anwendungen nehmen Sie bitte Kontakt zu uns auf.

Maven 2/3#

Die Bibliotheken können aus dem Intersult Maven Repository bezogen werden.

The XML Tools are accessible using the following ids:

group-idcom.intersult
artifact-idabraxas
version1.3-SNAPSHOT

The Maven Plugin is accessible under:

group-idcom.intersult
artifact-idabraxas-maven
version1.3-SNAPSHOT

The Remote-Service:

group-idcom.intersult
artifact-idabraxas-service
version1.3-SNAPSHOT

Goals#

Abraxas unterstützt verschiedene Goals, die in Maven gestartet werden können:

GoalBedeutung
generate-wsGeneriert Klassen, um einen Web-Service aufzurufen.
generate-test-wsGeneriert Test-Klassen, um einen Web-Service aufzurufen. Wird meist verwendet, um einen Web-Service zu testen, der im selben Projekt implementiert wurde. Durch Verwenden von URLs vom Type unit://... des ServletUnitProtocol, kann eine direkte Verbindung zum Service hergestellt werden.
generate-schemaGeneriert eine Schema-Datei aus einer oder mehreren Java-Klassen. Aus diesem Schema lassen sich später wieder Java-Klassen generieren. Durch Marshalling und Unmarshalling (Serialisierung nach XML) mittels der Klasse com.intersult.xml.Xml entsteht so kompatibles XML.
generate-test-schemaWie generate-schema, nur dass das Schema im Test-Resource-Scope von Maven erzeugt wird.
induce-schemaMit diesem Goal kann ein Schema aus einem XML abgeleitet werden.
induce-test-schemaWie induce-schema, nur dass das abgeleitete Schema im Test-Resource-Scope erzeugt wird.

Konfiguration#

In der Maven-Definition wird festgelegt, wie Java-Klassen aus WSDL- oder XSD-Links generiert werden. Der Eintrag für eine WSDL folgt dem Aufbau:
    <build>
        ...
        <plugins>
            ...
            <plugin>
	        <groupId>com.intersult</groupId>
	        <artifactId>abraxas-maven</artifactId>
	        <version>1.3-SNAPSHOT</version>
	        <executions>
	            <execution>
		        <goals>
		            <goal>generate-ws</goal>
		        </goals>
			<configuration>
	                    <services>
			        <service>
			    	    ...
			    	</service>
			    </services>
			</configuration>
	            </execution>
		<executions>
	    </plugin>
	    ...
	</plugins>
	...
    </build>

Unter <service> werden ein oder mehrere Services generiert. Unterhalb können weiter Parameter angegeben werden, die das Generieren der Servies steuern:

ParameterBedeutung
outputPathAusgabepfad für die generierten Klassen, in der Regel etwas wie ${project.build.directory}/generated-sources/weather
wsdlURL zur WSDL, zum Beispiel file:/${basedir}/src/main/resources/globalweather.wsdl oder kann auch direkt auf HTTP gehen. Durch Protocol-Plugins können auch weitere URLStreamHandler verwendet werden.
packageNameDer Name der zu generierenden Packages. Falls er nicht angegeben wird, wird er aus dem Target-Namespace aus der WSDL generiert.
ports *Liste der zu generierenden "Ports", falls der Wert nicht angegeben wird, werden alle WSDL-Ports generert. Beispiel <ports><port>GlobalWeatherSoap</port></ports>
operations* Zu generierende WSDL-Operations. Wird der Wert nicht angegeben, werden alle Operations generiert. Beispiel wäre <operations><operation>GetWeather</operation></operations>
xmlConfigKonfiguration der XML-Verarbeitung durch Abraxas-XML, wie unter dem Abschnitt XML-Konfiguration beschrieben ist.
protocolZu verwendender URLStreamHandler für den WSDL-URL, zum Beispiel <protocol implementation="com.intersult.testing.ServletUnitProtocol">
mappingsHier können zusätzliche Mappings beim Erzeugen der Java-Klassen definiert werden. Dies ist ein sehr fortgeschrittenes Feature und für gewöhnlich nicht notwendig, da die generierten Klassen abgeleitet und im RemoteClient registriert werden können.

Bei der Angabe von Ports und Operations werden (momentan) trotzdem alle Klassen aus dem eingebetteten XSD-Schema generiert.

XML-Generieren und Verarbeiten#

Die Klasse com.intersult.xml.Xml ist der Einstiegspunkt für das Umwandeln von XML in Objekte (Unmarshalling) und Objekte in XML (Marshalling).

Anwendung#

Es braucht sich um keine speziellen Objekte handeln, sogenannte Pojos (Plain Old Java Objects) sind ausreichend, also keine besondere Superklasse oder Interface notwendig.

Das Marshalling und Unmarshalling stellt ein Default-Verhalten zur Verfügung, das einen sehr weiten Bereich von denkbaren Java-Objekten abdeckt. Darüber hinaus besteht die Möglichkeit durch einige Annotations das Marshalling zu steuern.

Datentypen#

Neben primitiven Datentypen und verschachtelten Typen serialisiert der Marshaller auch Arrays, Lists, Maps sowie benutzerdefinierte Datentypen.

Eigene Datentypen können immer durch die voll qualifizierte Angabe der Java-Klasse instantiiert werden:

Xml.unmarshall("<com.intersult.Test>...</com.intersult.Test>");

Hinweis: Die Datentypen und Namen enthaltener Felder werden durch Reflection gemappt.

In den meisten Fällen hat man keine Java-Klassennamen als XML-Elemente. Daher stellt Abraxas die Möglichkeiten zum Mapping zur Verfügung.

Die einfachste Möglichkeit ist die Annotation der betreffenden Klasse mit XmlNamespace:

@XmlNamespace(value = "http://java.sun.com/xml/ns/javaee", localPart = "descriptionType")
public class DescriptionType {
    ...
}

Diese Klasse wird dann in der XmlConfig registriert:

    XmlConfig config = new XmlConfig();
    config.register(DescriptionType.class);
    DescriptionType descriptionType = (DescriptionType)Xml.unmarshall("<descriptionType>...</descriptionType>", config);

Alternativ kann auch der Class-Path nach XmlNamespace gescannt werden:

    XmlConfig config = new XmlConfig();
    config.scanClassPath();

Hinweis: Das Scannen des Class-Path birgt dir Gefahr, die Übersicht zu verlieren, welche Klassen für die aktuelle Operation registriert ist. Hat man eine große, veränderliche Menge von Klassen, kann dies von Vorteil sein. Class-Path-Scanning wird in Zukunft noch weiter ausgebaut werden.

Es können auch Klassen registriert werden, die nicht annotiert sind:

    XmlConfig config = new XmlConfig();
    config.getMapping().put(new QName("someName", "type"), SomeClass.class);

Hinweis: Es handelt sich um eine Bidirectional Map (BidiMap), mit der sowohl das Marshalling als auch das Unmarshalling definiert wird. Es werden daher nur Einträge akzeptiert, welche für beide Richtungen eindeutig sind (auch als ein-eindeutig oder bijektiv bezeichnet). Mit einer weiteren Methode put(K key, V value, boolean reverse) können Einträge hinzugefügt werden, die nur einen QName auf eine Java-Klasse mappen.

Beispiel für Unmarshalling#

    String xml = "<java.lang.String>Test</java.lang.String>";
    String string = (String)Xml.unmarshall(xml);
    System.out.println(string);

Erklärung: Die XML "Datei" wird direkt als String im Java-Code erzeugt und der lokalen Variablen xml zugewiesen. Danach wird mit die Methode marshall der Klasse com.intersult.xml.Xml aufgerufen, welche Strings, Streams und andere Quellen in Java-Objekte umwandelt (sog. Unmarshalling).

Beispiel für Marshalling und Unmarshalling#

Der XmlMarshaller ist ein sehr flexibles Werkzeug, das über die Klasse com.intersult.xml.Xml angesprochen wird. Diese Klasse stellt statische Methoden für das Umwandeln von Java-Objekten in XML (marshal) und von XML in Java-Objekte (unmarshal) zur Verfügung. Die Klasse ist dafür ausgelegt, sehr einfach genutzt zu werden.

Folgendes Code-Beispiel zeigt einen Unmarshal-Marshal-Roundtrip:

    Foo objectInput = new Foo();
    objectInput.setValue("Test-Wert");
    String xml = Xml.marshall(objectInput);
    System.out.println(xml);
    Foo objectOutput = (Foo)Xml.unmarshall(xml);
    System.out.println(objectOutput.getValue());

Mit Foo.java:

public class Foo {
	private String value;

	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}

XML-Konfiguration#

Die XmlConfig-Klasse wird verwendet, um das XML-Marshalling und -Unmarshalling zu steuern. Es stehen folgende Parameter zur Verfügung:

ParameterDefaultBedeutung
validatetrueDamit kann das validieren gelesener XML-Dateien durch angegebene XSD- und DTD-Dateien abgeschaltet werden.
writeEmptyfalseLegt fest, ob Null-Objekte geschrieben werden sollen.
useDefaultfalseDamit kann das Verarbeiten von Default-Annotationen eingeschaltet werden.
writeIdfalseLegt fest ob extra Attribute für IDs bei Arrays und Maps geschrieben werden sollen.
encodingUTF-8Legt das Encoding fest.
writeTypeNONELegt fest ob Java-Class-Namen geschrieben werden sollen. Bei NONE werden nur die einfachen Namen der Java-Properties geschrieben, außer beim Wurzel-Element falls dies keine Name- oder Namespace-Annotation enthält. Bei DERIVATES werden Klassennamen geschrieben, falls das Attribut von der Definition abweicht, also abgeleitet ist. Bei ALL werden die Typnamen immer geschrieben.
throwUnknowntrueDamit können Exceptions abgeschaltet werden, die erzeugt werden wenn Attribut- und Elementnamen nicht auf die Java-Klassen gemappt werden können.
mapping-Mappings für das Marshalling/Unmarshalling
classLoaderXmlUnmarshaller.class.getClassLoader()ClassLoader zum Laden von Klassen.
attributeHandlersInterne HandlerHandler welche die XML-Attribute verarbeiten, wie Namespace-Includes.
prettytrueDamit kann das Formattieren des generierten XML-Codes abgeschaltet werden.
formatMapInterne FormateRegistrierung von Klassen zur Formatierung von speziellen Objekten.
namespaceStack-Zugriff auf den NamespaceStack der XML-Verarbeitung
processIncludestrueDamit können Schema-Include-Anweisungen komplett abgeschaltet werden.
absoluteIncludestrueDamit können absolute URLs bei Schema-Includes ausgeschaltet werden.
throwDuplicatetrueDamit können die Exceptions bei konourrierenden Array- und Map-Einträgen abgeschaltet werden.
usePrefixfalseLegt fest, ob die SOAP-Message ein Namespace-Prefix bekommen soll.
processEnumtrueLegt fest, ob Enums generiert werden sollen.
throwFaulttrueMöglichkeit zum Abschalten von Exceptions bei SOAP-Faults.
serializablefalseDamit können alle generierten Klassen serialisierbar gemacht werden.
handleIdstrueLegt fest, ob ID-Angaben in Arrays und Maps verarbeitet werden.
autoCdatatrueLegt fest, ob bei Bedarf automatisch CDATA-Elemente generiert werden sollen.
loggingfalseLegt fest, ob die XML-Kommunikation mitgeloggt werden soll.
annotationstrueLegt fest ob Annotations generiert werden sollen.
proxyProxy.NO_PROXYZu verwendendes java.net.Proxy
connectTimeout-Timeout für die TCP-Connection.
readTimeout-Timeout für das Lesen von der TCP-Connection.
collectNamespacetrueLegt fest ob beim Generieren von XML die Namespace-Definitionen zuerst gesammelt werden und auf Dokumentenebene ausgegeben werden. Das reduziert Namespace-Definitionen und verkürzt damit das generierte XML.
unwrapfalseAuspacken von Response-Klassen. Kann zu Fehlern führen, wenn die SOAP-Response-Messages mehrere Elemente enthalten.
transparentNtlmtrueExperimentelles Feature zum Unterbinden von transparenter NTLM-Authentifizierung, falls der Client auf Windows-Rechnern läuft.
induceTypestrueLegt fest, ob bei der Schema-Induktion Typen wie Integer oder Datum automatisch erkannt werden oder ob standardmäßig xsd:string verwendet wird.
induceNillablefalseSteuert ob bei der Schema-Induktion Felder als nillable gekennzeichnet werden.
valueInducerIntegerInducer, UrlInducer, DateTimeInducerListe von Objekten zum feststellen von Datentypen, die das Interface ValueInducer implementieren. Es handelt sich um eine Liste, die angepasst werden kann, um das Feststellen von Datentypen bei der Schema-Induktion zu unterstützen.
minStrategyLAXLegt fest, mit welcher Strategie die minimale Anzahl von Child-XML-Elementen induziert wird (Schema-Attribut minOccurs). Default ist die LAX-Strategy, die ein Mindestauftreten eines Elements von 1 verlangt, wenn das Element in allen Fällen mindestens einmal vorkam und mindestens zwei Fälle vorliegen.
maxStrategyLAXLegt fest, mit welcher Strategie die maximale Anzahl von Child-XML-Elementen induziert wird (Schema-Attribut maxOccurs). Default ist die MEDIUM-Strategie, die das Maximalauftreten eines Elements von UNBOUNDED erlaubt, sobald in einem Fall mindestens zwei Elemente aufgetreten sind. Hier ist keine LAX-Strategie sinnvoll, da maxOccurs per default als 1 angenommen wird, somit das Fehlen des Attributs zu einem Fehler beim Parsen der zuständigen XML führen würde.
useStragegyLAXLegt die Strategie fest, mit der das use-Attribut "required" bei Attributen induziert wird. Default ist die LAX-Strategie, die required setzt, wenn in das attribut in allen Fällen vorhanden war und mindestens zwei fälle vorliegen.
generateXsdElementstrueLegt fest, ob die im Schema enthaltenen Wurzelelemente generiert werden sollen. Dies macht Sinn, falls sich die Elementnamen von den Typnamen unterscheiden und doppelter Code verhindert werden soll.
keepXmlElementsfalseLegt beim Generieren von Klassen fest, ob in ComplexType die Informationen zu Nodes vom Typ Element erhalten bleiben. Konkret werden XmlNamespace-Annotationen für die Element-Nodes erzeugt, die das Marshalling steuern.
qualifyElementstrueLegt fest, ob untergeordnete XML-Elemente ebenfalls qualifiziert werden (also einen Namespace-Prefix erhalten).
unqualifiedNamespacetrueBei unqualified Elements (qualifiedElements = false) wird normaler Weise der qualifizierte Namespace in Form von xmlns:tns und xmlns ausgegeben, dies tritt beim default Wert von true ein. Wird die Ausgabe von xmlns nicht gewünscht, sondern nur von xmlns:tns, kann das Attribut auf false gesetzt werden.
qualifyAttributesfalseLegt fest, ob XML-Attribute qualifiziert werden (mit Namespace-Prefix versehen).

Web Service (SOAP)#

Der Client-Generator für SOAP-Service verwendet intern den Intersult Xsd-Generator, der auch eigenständig nutzbar ist. Damit ist es möglich, Remote-Aufrufe über Intra- und Internet abzuwickeln. Eine große Zahl von Services, die auf unterschiedlichsten Technologien (auch nicht-Java) basieren, verwenden SOAP. Damit steht eine sehr große Menge an Services und Providern zur Verfügung.

Um einen Web-Service auf SOAP-Basis anzusprechen, braucht man zunächst Java-Klassen, die die entsprechenden Daten übertragen. Diese werden aus der sogenannten WSDL (Web Service Description Language) generiert, die der SOAP-Service zur Verfügung stellt. Darin ist beschrieben, welche Methoden der Service anbietet und welche Objekte für die Kommunikation verwendet werden.

Die Generierung von Web Services aus WSDL-Dateien baut zum Teil auf der Generierung von XML-Schemata aus XSD-Dateien auf. Durch folgende Konfiguration kann ein Web Service Client generiert werden:

<project>
    ...
    <build>
        ...
        <plugins>
            ...
        	<plugin>
	            <groupId>com.intersult</groupId>
		    <artifactId>abraxas-maven</artifactId>
		    <version>1.3-SNAPSHOT</version>
		    <executions>
			<execution>
		            <goals>
				<goal>generate-ws</goal>
			    </goals>
			    <configuration>
				<services>
			    	    <service>
				        <outputPath>${project.build.directory}/generated-sources/weather</outputPath>
				        <wsdl>http://www.webservicex.net/globalweather.asmx?WSDL</wsdl>
				        <packageName>net.webservicex.globalweather</packageName>
				    </service>
			        </services>
			    </configuration>
			</execution>
		    </executions>
	        </plugin>
	    ...
	</plugin>
	...
    </build>
    ...
</project>

Der Global Weather Service generiert einen Service, der durch folgenden Code ansprechbar ist:

    GlobalWeatherSoap soap = new GlobalWeatherSoap();
    GetWeatherResponse weather = soap.getWeather("nuernberg", "germany");
    System.out.println(weather.getGetWeatherResult());

XML-Schema (XSD)#

Abraxas kann Java-Dateien auch aus XSD-Dateien generieren. Dies kann oft nützlich sein, wenn die XML-Dateien zur Konfiguration benutzt werden (Also keine XML-Kommunikation über SOAP erfolgt).

Dies kann in der pom.xml dann so aussehen:

<plugin>
	<groupId>com.intersult</groupId>
	<artifactId>abraxas-maven</artifactId>
	<version>1.3-SNAPSHOT</version>
	<executions>
		<execution>
			<id>generate-schema</id>
			<goals>
				<goal>generate-schema</goal>
			</goals>
			<configuration>
				<schemas>
					<schema>
						<xsdPath>${project.build.directory}/generated-sources/web</xsdPath>
						<xsd>http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd</xsd>
					</schema>
				</schemas>
			</configuration>
		</execution>
	</executions>
</plugin>

Das Beispiel zeigt, wie das XML-Schema der web.xml Version 2.5 über HTTP gestreamt wird und darauf Java-Klassen generiert. Dies kann eingesetzt werden, um Java-Klassen zu erzeugen mit der eine web.xml abgebildet werden könnte mit dem Zweck diese auszulesen oder zu generieren.

Java-Generierung#

Ein XML-Schema kann auch aus Java heraus generiert werden. Dies wird zum Beispiel implizit beim Verwenden von Web-Services durchgeführt:
XsdWriter writer = new XsdWriter(new QName(
    "http://intersult.com/test", XMLConstants.DEFAULT_NS_PREFIX, "test"));
writer.addElement(SomeClass.class);
writer.write(System.out);

Erklärung: Über Reflection wird aus der angegebenen Klasse ein Schema generiert. Als Reflection wird dabei Intersult Coder verwendet, der zusätzlich die Reihenfolge von Methoden befolgt und Parameternamen von Methoden ermittelt.

Hinweis: Referenziert die Klasse andere Klassen, werden diese ebenfalls ins Schema geschrieben bis hinunter zu primitive Klassen, welche durch XSD-Types abgebildet werden. String wird zu xsd:string etc.

Warnung: Durch das rekursive Generieren kann eine beträchtliche Menge von Klassen entstehen, alleine durch das angeben einer einzelnen Klasse. Die verwendeten Klassen sollten daher explizit für das Generieren eines Schemas angepasst sein.

Protokolle#

Abraxas arbeitet mit URL-Connections, ist daher sehr verträglich und unterstützt damit bereits eine Grundzahl von Protokollen. Allesdings kann es sinnvoll sein, zusätzliche Protokolle zu verwenden, zum Beispiel wenn Code aus einer HttpUnit oder anderen Quellen generiert werden soll. Dies ist nützlich, wenn Tests für SOAP-Services geschrieben werden sollen.

Lokale Protokoll-Handler#

Die Protokoll-Handler können in der pom.xml lokal für den spezifischen URL verwendet werden:
	<service>
		<outputPath>${project.build.directory}/generated-sources/test</outputPath>
		<wsdl>unit://localhost/remote/HelloService?wsdl</wsdl>
		<packageName>com.intersult.hello</packageName>
		<xmlConfig>
			<unwrap>true</unwrap>
		</xmlConfig>
		<protocol implementation="com.intersult.testing.ServletUnitProtocol">
			<webXmlFile>${basedir}/src/main/webapp/WEB-INF/web.xml</webXmlFile>
		</protocol>
	</service>

Beim Aufruf des Service kann der Handler ebenfalls lokal verwendet werden:

	ServletUnitProtocol protocol = new ServletUnitProtocol();
	URL serviceUrl = new URL(null, HelloService.SERVICE_URL, protocol);
	HelloService helloService = new HelloService(serviceUrl, Transport.SOAP);

Durch globale Factory#

Es können beliebige Protokolle hinzugefügt werden, wenn diese ProtocolFactory unterstützen:
<plugin>
	<groupId>com.intersult</groupId>
	<artifactId>abraxas-maven</artifactId>
	<version>1.3-SNAPSHOT</version>
	<executions>
		<execution>
			<id>generate-ws</id>
			<goals>
				<goal>generate-ws</goal>
			</goals>
			<configuration>
				<protocols>
					<com.intersult.testing.httpunit.HttpUnitProtocol>
						<webXmlFile>${basedir}/src/main/webapp/WEB-INF/web.xml</webXmlFile>
						<translateProtocol>false</translateProtocol>
					</com.intersult.testing.httpunit.HttpUnitProtocol>
				</protocols>
				<services>
					<service>
						<outputPath>${project.build.directory}/generated-sources/test</outputPath>
						<wsdl>unit://localhost/remote/HelloService?wsdl</wsdl>
						<packageName>com.intersult.hello</packageName>
						<xmlConfig>
                				    <unwrap>true</unwrap>
						</xmlConfig>
					</service>
				</services>
			</configuration>
		</execution>
	</executions>
	<dependencies>
		<dependency>
			<groupId>com.intersult</groupId>
			<artifactId>testing</artifactId>
			<version>1.0-SNAPSHOT</version>
		</dependency>
	</dependencies>
</plugin>

Das Artifact mit dem entsprechenden Protocol wird also als Dependency des Abraxas Plugin hinzugefügt. Damit sind die entsprechenden Klassen während der Ausführung des Abraxas Plugins im Classpath zugreifbar und können in der Konfiguration unter "protocols" hinzugefügt und selbst mit Parametern versehen werden.

Es ist nicht immer sinnvoll, Protokolle durch die ProtocolFactory systemweit zu registrieren.

Schema-Induktion#

Abraxas kann aus einer XML-Datei ein Schema generieren. Das Generieren eines Schemas wird Induktion genannt, weil anhand von Beispiel XML Dateien auf das Schema geschlossen wird. Daher ist zu beachten, das dies nur Annahmen sind und nie die wirkliche Intention des dahinter liegenden Schemas erfassen können.

Arten von Induktion#

Aus einem übergebenen XML-Dokument werden die Datentypen und die Häufigkeit des Auftretens der Elemente induziert. Induktion bedeutet, dass Annahmen getroffen werden, die nicht zwingend richtig sind.

Häufigkeit von Elementen und Attributen#

Die Häufigkeit von Elementen und Attributen werden durch Strategien induziert, die in der XmlConfig angepasst werden können. Induzieren bedeutet, die im gegebenen XML-Dokument aufgetretenen Fälle werden gezählt und dann mit Hilfe einer Strategie entschieden, wie das Attribut gesetzt werden soll.

Die Induktionsstrategien sind generell so ausgelegt, dass das Parsen des zur Induktion verwendeten XML-Dokuments positiv verläuft. Anders ausgedrückt, erzeugt die Induktion ein XML-Schema, innerhalb dessen das zur Induktion verwendete XML-Dokument gültig ist. Dies gilt für alle mitgelieferten Strategien.

Eigene Strategien#

Bei den Strategien handelt es sich um abstrakte Basisklassen, für die eine Reihe von Implementierungen mitgeliefert werden. Jeweils eine wurde in der XmlConfig als Default-Strategie ausgewählt, die verwendet wird, falls der Benutzer diese nicht selbst anpasst.

Da die Strategien vom Nutzer abgeleitet werden können, können damit Schemata erzeugt werden, innerhalb derer das gegebene XML-Dokument ungültig wird. Das Ableiten der Strategien ist daher mit der entsprechenden Vorsicht zu verwenden.

Parameter#

Die Induktion aus Maven wird durch das Plugin com.intersult:abraxas-maven durchgeführt. Dabei werden folgende Parameter unterstützt:

ParameterBeschreibung
resourceRootErmöglicht das Einbinden als Resource-Pfad in den Build-Prozess. Der Parameter ist optional, durch Angabe ist es möglich die generierten XSD-Dateien in das Artifakt einzubinden. Beispiel wäre <resourceRoot>${project.build.directory}/generated-resources/xsd</resourceRoot>
protocolsErmöglicht das Einbinden globaler Protocols, wie bei generate-ws oder generate-schema.
outputFileDatei des zu generierenden Schemas, der Pfad wird komplett angelegt falls er noch nicht existiert. Beispiel wäre <outputFile>${project.build.directory}/generated-resources/xsd/simple.xsd</outputFile>
xmlURL auf eine XML-Datei, aus der das Schema generiert werden soll.
targetNamespaceOptional kann der Target-Namespace manuell festgelegt werden. Standardmäßig wird er aus der XML-Datei entnommen, falls dort xmlns oder xmlns:<tns> Angaben vorhanden sind.
prefixOptional kann das zu verwendende Prefix angegeben werden. Andernfalls wird es aus der verarbeiteten XML-Datei entnommen.
xmlConfigZugriff auf die XmlConfig, mit der Möglichkeit entsprechende Werte zu setzen. Insbesondere interessant sind hier die Strategie-Angaben zur Induktion von Anzahl und Typen.
protocolOptionale Angabe eines Protocols, das beim Zugriff auf die XML-Datei benutzt wird.

Beispiele#

Maven#

Durch eine Konfiguration in der pom.xml kann eine Schema induziert werden:
	<plugin>
		<groupId>com.intersult</groupId>
		<artifactId>abraxas-maven</artifactId>
		<version>1.3-SNAPSHOT</version>
		<executions>
			<execution>
				<id>induce-schema</id>
				<goals>
					<goal>induce-schema</goal>
				</goals>
				<configuration>
					<inducers>
						<inducer>
							<xml>file:/${basedir}/src/main/resources/simple.xml</xml>
							<outputFile>${project.build.directory}/simple.xsd</outputFile>
						</inducer>
					</inducers>
				</configuration>
			</execution>
		</executions>
	</plugin>

Die XSD-Datei wird hier im Target generiert. Falls die Ressource-Root zum Build-Path hinzugefügt werden soll, kann dies mit dem Parameter <resourceRoot>...path...</resourceRoot> gemacht werden.

Ansonst kann die erzeugte XSD auch verwendet werden, um mit generate-schema in einem weiteren Execution-Schritt die Java-Klassen zu generieren.

Java#

Der SchemaInducer kann auch aus Java heraus benutzt werden, um Schema-Informationen zu generieren:
	SchemaInducer inducer = new SchemaInducer();
	InputStream inputStream = getClass().getResourceAsStream("induce.xml");
	Xsd xsd = inducer.induce(inputStream);
	xsd.write(System.out);

Remote-Service#

Mit Abraxas können auch Remote-Services nach außen hin zur Verfügung gestellt werden. Die Services bauen auf den Servlet-Standard auf und sind als WAR-Datei auf einem Application Server deploybar.

Anwendung#

Der Abraxas Remote-Service ist auf einfache Handhabung ausgelegt. Es ist keine Konfiguration nötig, außer das Registrieren des RemoteServlet in der web.xml und das Bekanntmachen der Service-Klassen durch den Init-Parameter "service".

Init-ParameterBeispielBedeutung
servicecom.intersult.xml.remote.HelloServiceDurch Komma getrennte Liste von Java-Klassen, die sich im Class-Path des Projekts befinden und als Service angeboten werden sollen.
transportSOAPDurch Komma getrennte Liste von Transport-Protokollen, die für den Service angeboten werden sollen. Standard ist SOAP, andere Werte werden zunächst als Felder in der Klasse com.intersult.xml.remote.Transport gesucht, dann als voll qualifizierte Java-Klassen. Eigene Transport-Protokolle können von com.intersult.xml.remote.Transport abgeleitet werden.
service-factoryMit diesem optionalen Parameter kann eine Service-Factory angegeben werden. Default ist com.intersult.xml.remote.ReflectiveServiceFactory, die Service-Beans werden durch Java-Reflection erzeugt. Hier sind andere Factorys denkbar, die zum Beispiel Beans im JSF- oder Spring-Kontext erzeugen.

Hintergrund#

Der Service wird über Reflection erzeugt. Im Gegensatz zu den meisten SOAP- und Remote-Service-Frameworks sind nur wenig Annotationen erforderlich, hauptsächlich der @XmlNamespace der zum Erzeugen der WSDL verwendet wird.

Es wird davon ausgegangen, dass Service-Klassen zwar POJOs sind (also einfache Java-Klassen ohne spezielle Service-Superklasse). Allerdings wird auch angenommen, dass eine Service-Klasse speziell als Service-Geschrieben wurde und keine Methoden enthält, die nicht exponiert werden sollen. Ansonst wird empfohlen, eine solche Klasse anzulegen.

Für die Reflection wird Intersult Reflector verwendet, dadurch werden zum Beispiel auch Parameter-Namen der Service-Methoden angezeigt.

Beispiel#

In der web.xml wird das RemoteServlet registriert:
    <servlet>
        <servlet-name>RemoteServlet</servlet-name>
        <servlet-class>com.intersult.xml.remote.RemoteServlet</servlet-class>
        <init-param>
        	<param-name>service</param-name>
        	<param-value>com.intersult.xml.remote.HelloService</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    	<servlet-name>RemoteServlet</servlet-name>
    	<url-pattern>/remote/*</url-pattern>
    </servlet-mapping>

Der zugehörige HelloService.java kann zum Beispiel so aussehen:

@XmlNamespace(value = "http://intersult.com/test")
public class HelloService {
	public String[] hello(String name) {
		return new String[] {
			"Hello " + name,
			"how are you?"
		};
	}
	
	public void fail() {
		throw new IllegalArgumentException("Some failure");
	}
}

Über die URL http://localhost/remote/HelloService?wsdl kann dann die WSDL abgerufen werden.

Stacktrace#

Sowohl Abraxas Remote-Service als auch der Remote-Client unterstützen das Übertagen von Stacktraces. Dies kann gerade beim Entwickeln von Web-Services eine erhebliche Erleichterung sein.

Folgendes Beispiel zeigt einen Stacktrace, bei dem auf der Service-Seite versäumt wurde eine Hibernate-Session zu öffnen:

Fault: org.hibernate.SessionException: Session is closed!: Session is closed!
	at com.intersult.xml.remote.RemoteClient.invoke(RemoteClient.java:94)
	at com.intersult.subflow.test.Subflow.getProjects(Subflow.java:43)
	at com.intersult.subflow.test.SubflowServiceTest.testProducts(SubflowServiceTest.java:50)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:236)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:134)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:113)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.plugin.surefire.InPluginVMSurefireStarter.runSuitesInProcess(InPluginVMSurefireStarter.java:74)
	at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeProvider(AbstractSurefireMojo.java:194)
	at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAllProviders(AbstractSurefireMojo.java:176)
	at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAfterPreconditionsChecked(AbstractSurefireMojo.java:135)
	at org.apache.maven.plugin.surefire.AbstractSurefireMojo.execute(AbstractSurefireMojo.java:98)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:319)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
Caused by: com.intersult.xml.error.RemoteException: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at com.intersult.code.MethodClass.invoke(MethodClass.java:28)
	at com.intersult.xml.remote.WrappedMethodClass.invoke(WrappedMethodClass.java:32)
	at com.intersult.xml.remote.WrappedMethodClass.invoke(WrappedMethodClass.java:14)
	at com.intersult.code.MethodInstance.invoke(MethodInstance.java:15)
	at com.intersult.xml.remote.RemoteService.invoke(RemoteService.java:98)
	at com.intersult.xml.remote.RemoteServlet.service(RemoteServlet.java:53)
	at com.intersult.servlet.InvocationContext.service(InvocationContext.java:61)
	at com.intersult.servlet.ServletContainer.getResponse(ServletContainer.java:126)
	at com.intersult.testing.ServletUnitConnection.getInputStream(ServletUnitConnection.java:57)
	at com.intersult.util.net.UrlConnection.getInputStream(UrlConnection.java:38)
	at com.intersult.xml.soap.SoapTransport.unmarshal(SoapTransport.java:78)
	at com.intersult.xml.remote.RemoteClient.invoke(RemoteClient.java:78)
	... 55 more
Caused by: com.intersult.xml.error.RemoteException: org.hibernate.SessionException: Session is closed!
	at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:129)
	at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1593)
	at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:374)
	at com.intersult.subflow.hibernate.HibernateDynamicList.list(HibernateDynamicList.java:35)
	at com.intersult.subflow.SubflowService.getProjects(SubflowService.java:38)
	... 71 more

Erklärung: Der Stacktrace wird nahtlos in den Lokalen Stacktrace integriert und dann als Exception-Klasse "Fault" geworfen.