This page (revision-87) was last changed on 16-Aug-2019 12:51 by Dieter Käppel

This page was created on 18-Aug-2009 22:25 by Dieter Käppel

Only authorized users are allowed to rename pages.

Only authorized users are allowed to delete pages.

Page revision history

Version Date Modified Size Author Changes ... Change note
87 16-Aug-2019 12:51 53 KB Dieter Käppel to previous Technologie ==> Technologien
86 16-Aug-2019 12:51 53 KB Dieter Käppel to previous | to last
85 28-Nov-2015 11:16 53 KB Dieter Käppel to previous | to last
84 28-Nov-2015 11:16 53 KB Dieter Käppel to previous | to last
83 18-Feb-2015 09:14 52 KB Dieter Käppel to previous | to last
82 18-Feb-2015 08:51 52 KB Dieter Käppel to previous | to last
81 25-Jul-2014 11:27 52 KB Dieter Käppel to previous | to last

Page References

Incoming links Outgoing links

Version management

Difference between version and

At line 1 changed one line
[Intersult] [Abraxas] ist ein Werkzeug zur Kommunikation mit [SOAP] und anderen Protokollen. [Abraxas] besteht im wesentlichen aus folgenden Bestandteilen:
[Intersult] [Abraxas] - M2M-Kommunikation und XML-Dateien im Netzwerk. Mit [Abraxas] wird die Kommunikation von Machine-2-Machine erstmals verständlich und einfach zu nutzen. Auch bei komplexen Anforderungen.
At line 3 added 25 lines
%%(
text-align: center;
margin: 20px 0;
font-size: larger;
)
Testen sie [Abraxas] noch heute - und sehen sie, wie einfach XML werden kann!
%%
[Technologisch|Technologien] handelt es sich um das Einlesen und Speichern im XML-Format, die Kommunikation über [SOAP], REST, sowie das Generieren von Java-Klassen, XML-Schema (XSD), WSDL (SOAP- und andere Web-Services), Serialisieren (Marshalling) von Java-Objekten in XML und umgekehrt.
Bugs und Feature-Requests können über [https://www.sub-flow.com/] eingegeben werden.
Ein Beispiel-Projekt mit [Maven] ist als ZIP-Datei [Abraxas/abraxas-client-1.4.zip] verfügbar.
!!!Inhalt
[{TableOfContents numbered=true}]
!!!Tutorials
[Abraxas] Example Tutorial mit SOAP-Service, SOAP-Client und JUnit-Test innerhalb eines Maven-Projekts:
[{Flash src='https://www.youtube.com/v/LRnZMHzXO3c' controls='false' play='true'}]
!!!Bestandteile
[Abraxas] besteht im Wesentlichen aus folgenden Bestandteilen:
At line 9 changed one line
* __XML-Tools:__ [Abraxas] kann auch genutzt werden, um Java-Objekte nach XML zu konvertieren und umgekehrt. Es werden Pojos unklusive Arrays, Listen und Maps unterstützt. Zusätzlich existieren einige Annotationen mit denen die Umwandlung an spezielle Bedürfnisse angepasst werden kann.
* __XML-Tools:__ [Abraxas] kann auch genutzt werden, um Java-Objekte nach XML zu konvertieren und umgekehrt. Es werden Pojos inklusive Arrays, Listen und Maps unterstützt. Zusätzlich existieren einige Annotationen mit denen die Umwandlung an spezielle Bedürfnisse angepasst werden kann.
At line 11 changed one line
!Was macht Abraxas?
!!!Was macht Abraxas?
At line 14 changed one line
Das XML-Paket kann unter [Maven 2|Maven] direkt aus dem [Intersult Maven Repository] bezogen werden. Für [Rechte|Rechtliches] und Einsatz in kommerziellen Anwendungen nehmen Sie bitte [Kontakt|Intersult] zu uns auf.
Das XML-Paket kann unter [Maven 2/3|Maven] direkt aus dem [Intersult Maven Repository] bezogen werden. Für [Rechte|Rechtliches] und Einsatz in kommerziellen Anwendungen nehmen Sie bitte [Kontakt|Intersult] zu uns auf.
At line 16 changed one line
!Maven2/3
!!!Maven 2/3
Die Bibliotheken können aus dem [Maven Central Repository|http://search.maven.org/] oder dem [Intersult Maven Repository] bezogen werden.
At line 21 changed one line
|version|1.3-SNAPSHOT
|version|1.4
At line 27 changed one line
|version|1.3-SNAPSHOT
|version|1.4
At line 33 changed one line
|version|1.3-SNAPSHOT
|version|1.4
At line 35 changed one line
Simply add the plugin repository to your pom.xml to allow Maven download the plugin:
!!Goals
[Abraxas] unterstützt verschiedene Goals, die in Maven gestartet werden können:
At line 65 added 13 lines
||Goal||Bedeutung
|generate-ws|Generiert Klassen, um einen Web-Service aufzurufen.
|generate-test-ws|Generiert 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-schema|Generiert Java-Klassen aus einem reinen Schema, ohne Web-Service Aufrufe. Die generierten Klassen sind kompatibel für das Marshalling und Unmarshalling (Serialisierung nach XML) mittels com.intersult.xml.Xml.
|generate-test-schema|Wie generate-schema, nur dass die Klassen im Test-Scope von Maven erzeugt werden.
|induce-schema|Mit diesem Goal kann ein Schema aus einem XML abgeleitet werden.
|induce-test-schema|Wie induce-schema, nur dass das abgeleitete Schema im Test-Resource-Scope erzeugt wird.
|export-schema|Damit können Schema-Dateien (XSD) für Klassen aus dem aktuellen Projekt generiert werden. Natürlich können auch Klassen aus JARs herangezogen werden. Das Plugin wird standardmäßig in der Phase process-classes ausgeführt.
|export-test-schema|Wie export-schema, nur dass der Export für Test-Klassen ausgeführt wird. Die Ausfürungs-Phase ist dabei process-test-classes.
!!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:
At line 38 changed 3 lines
<project>
...
<pluginRepositories>
<build>
At line 42 changed 5 lines
<pluginRepository>
<id>intersult-repository</id>
<name>Intersult Repository</name>
<url>https://intersult.com/svn/public/maven</url>
</pluginRepository>
<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>
At line 48 changed 3 lines
</pluginRepositories>
...
</project>
</build>
At line 53 changed 2 lines
!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.
Unter <service> werden ein oder mehrere Services generiert. Unterhalb können weiter Parameter angegeben werden, die das Generieren der Servies steuern:
At line 56 changed one line
!Anwendung
||Parameter||Bedeutung
|outputPath|Ausgabepfad für die generierten Klassen, in der Regel etwas wie ${project.build.directory}/generated-sources/weather
|wsdl|URL 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.
|packageName|Der 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>
|xmlConfig|Konfiguration der XML-Verarbeitung durch Abraxas-XML, wie unter dem Abschnitt XML-Konfiguration beschrieben ist.
|protocol|Zu verwendender URLStreamHandler für den WSDL-URL, zum Beispiel <protocol implementation="com.intersult.testing.ServletUnitProtocol">
|mappings|Hier 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.
!!Annotations
[Abraxas] unterstützt das Scanning des ClassPath. Dies kann innerhalb einer XmlConfig durch den Aufruf von scanClassPath() erreicht werden. Wie üblich sollte die XmlConfig wiederverwendet werden, da das Scannen des ClassPath aufwändig ist.
Gefunden werden Klassen, die mit @XmlNamespace annotiert sind. Diese werden dann automatisch in das Mapping (XmlConfig.mapping) eingetragen. Die hinzugefügten Klassen stehen dann für das Marshalling und Unmarshalling zur Verfügung.
Falls sich zusätzlich die Annotation @XmlService an der Klasse befindet, wird die Klasse als Remote-Service hinzugefügt. Falls die Java-Anwendung eine Web-Anwendung ist (deploytes WAR-File), können die so annotierten Web-Services nach hinzufügen des RemoteServlet in die web.xml verwendet werden.
!!XML Config Files
[Abraxas] unterstützt die Konfiguration durch Config-Files, die sich in META-INF/abraxas.xml befinden. Das Dateiformat entspricht dabei einer serialisierten XmlConfig-Klasse und wird über diese drüber geparst (sieht setRoot).
Wie beim ClassPath-Scanning werden die Config Files bei normaler XmlConfig durch den Aufruf scanConfigFiles() durchgeführt. Beim Verwenden von RemoteService geschieht dies automatisch, weil die web.xml wenig Spielraum zur Konfiguration lässt.
Hier ein Beispiel für eine /META-INF/abraxas.xml:
{{{
<?xml version="1.0" encoding="UTF-8"?>
<xmlConfig xmlns="http://intersult.com/abraxas/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://intersult.com/abraxas/config http://intersult.com/xml/abraxas.xsd">
<scanClassPath>true</scanClassPath>
<wsdlStylesheet>wsdl.xsl</wsdlStylesheet>
<serviceFactory>com.intersult.subflow.SpringServiceFactory</serviceFactory>
</xmlConfig>
}}}
In der Konfigurations-Datei abraxas.xml sind im Wesentlichen die selben Attribute verfügbar, wie beim Verwenden der [Klasse XmlConfig|Abraxas#XML-Konfiguration].
Die XmlConfig kann auch beispielsweise mit Xml.marshall(config, config.getSuperConfig()) geschrieben werden. Das Ergebnis kann dann wie folgt aussehen:
{{{
<?xml version="1.0" encoding="UTF-8"?>
<xmlConfig xmlns="http://intersult.com/abraxas/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<validate>true</validate>
<writeEmpty>false</writeEmpty>
<useDefault>false</useDefault>
<writeId>false</writeId>
<encoding>UTF-8</encoding>
<writeType>NONE</writeType>
<throwUnknown>true</throwUnknown>
<mapping xsi:index="{http://intersult.com/test}Foo">com.intersult.xml.Foo</mapping>
<pretty>true</pretty>
<attributeHandlers xsi:index="0" xsi:type="com.intersult.xml.marshal.impl.IgnoreAttributeHandler"/>
<attributeHandlers xsi:index="1" xsi:type="com.intersult.xml.marshal.impl.NamespaceAttributeHandler"/>
<attributeHandlers xsi:index="2" xsi:type="com.intersult.xml.marshal.impl.KeyAttributeHandler"/>
<attributeHandlers xsi:index="3" xsi:type="com.intersult.xml.marshal.impl.SchemaHandler"/>
<attributeHandlers xsi:index="4" xsi:type="com.intersult.xml.marshal.impl.QNameAttributeHandler"/>
<attributeHandlers xsi:index="5" xsi:type="com.intersult.xml.marshal.impl.DefaultAttributeHandler"/>
<elementHandlers xsi:index="0" xsi:type="com.intersult.xml.marshal.impl.RootElementHandler"/>
<elementHandlers xsi:index="1" xsi:type="com.intersult.xml.marshal.impl.KeyHandler"/>
<elementHandlers xsi:index="2" xsi:type="com.intersult.xml.marshal.impl.DefaultElementHandler"/>
<formatMap xsi:index="java.util.Date">com.intersult.xml.marshal.impl.XmlDateFormat</formatMap>
<formatMap xsi:index="byte[]">com.intersult.util.format.Base64Format</formatMap>
<namespaceStack/>
<globalXsi>false</globalXsi>
<processIncludes>true</processIncludes>
<absoluteIncludes>true</absoluteIncludes>
<throwDuplicate>true</throwDuplicate>
<usePrefix>false</usePrefix>
<processEnum>true</processEnum>
<throwFault>true</throwFault>
<serializable>false</serializable>
<handleIds>true</handleIds>
<autoCdata>true</autoCdata>
<logging>false</logging>
<annotations>true</annotations>
<proxy/>
<collectNamespace>true</collectNamespace>
<unwrap>false</unwrap>
<transparentNtlm>true</transparentNtlm>
<induceTypes>true</induceTypes>
<induceNillable>false</induceNillable>
<valueInducer xsi:index="0" xsi:type="com.intersult.xml.xsd.induce.type.IntegerInducer"/>
<valueInducer xsi:index="1" xsi:type="com.intersult.xml.xsd.induce.type.UrlInducer"/>
<valueInducer xsi:index="2" xsi:type="com.intersult.xml.xsd.induce.type.DateTimeInducer"/>
<minStrategy>LAX</minStrategy>
<maxStrategy>MEDIUM</maxStrategy>
<useStrategy>LAX</useStrategy>
<generateXsdElements>true</generateXsdElements>
<keepXmlElements>false</keepXmlElements>
<qualifyElements>true</qualifyElements>
<unqualifiedNamespace>true</unqualifiedNamespace>
<qualifyAttributes>false</qualifyAttributes>
<useAttributeNamespace>true</useAttributeNamespace>
<primitivesAsAttribute>true</primitivesAsAttribute>
<scanClassPath>false</scanClassPath>
<scanPackage>com.intersult.some.package</scanPackage>
<serviceFactory>com.intersult.xml.remote.ReflectiveServiceFactory</serviceFactory>
<transports xsi:index="0" xsi:type="com.intersult.xml.soap.SoapTransport">
<prefix>soap</prefix>
<namespaceUri>http://schemas.xmlsoap.org/wsdl/soap/</namespaceUri>
</transports>
</xmlConfig>
}}}
__Warnung:__ Es sollte keine "vollständige" XmlConfig in abraxas.xml hinterlegt werden, da dies zu Problemen beim Überschreiben von Werten führen kann. Abraxas führt mehrere XmlConfig-Einträge selbständig zusammen. Es wird empfohlen, nur Elemente hinzuzunehmen, die ausdrücklich geändert oder hinzugefügt werden sollen.
!!Remote Service
Mit [Abraxas] können auch Web-Services zur Verfügung gestellt werden. Dies geschieht in einem Servlet-Container, z.B. Tomcat. Dort ist das RemoteServlet einzutragen:
{{{
<servlet>
<servlet-name>RemoteServlet</servlet-name>
<servlet-class>com.intersult.xml.remote.RemoteServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>RemoteServlet</servlet-name>
<url-pattern>/remote/*</url-pattern>
</servlet-mapping>
}}}
__Hinweis:__ Die zusätzliche Angabe von load-on-startup ist wichtig, damit das RemoteServlet die Web-Services findet und bereit stellt.
!!!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
At line 61 changed 2 lines
!Datentypen
Neben primitiven Datentypen und verschachtelten Typen serialisiert der Marshaller auch Arrays, Lists und Maps.
!!Datentypen
Neben primitiven Datentypen und verschachtelten Typen serialisiert der Marshaller auch Arrays, Lists, Maps sowie benutzerdefinierte Datentypen.
At line 246 added 57 lines
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.
At line 90 changed one line
Ein weiteres Beispiel für Unmarshalling:
!!XML-Konfiguration
Die XmlConfig-Klasse wird verwendet, um das XML-Marshalling und -Unmarshalling zu steuern. Es stehen folgende Parameter zur Verfügung:
At line 92 changed 5 lines
{{{
String input = "Test";
String xml = "<java.lang.String>" + input + "</java.lang.String>";
String output = (String)MarshalUtils.unmarshall(xml);
}}}
||Parameter||Default||Bedeutung
|validate|true|Damit kann das validieren gelesener XML-Dateien durch angegebene XSD- und DTD-Dateien abgeschaltet werden.
|writeEmpty|false|Legt fest, ob Null-Objekte geschrieben werden sollen.
|useDefault|false|Damit kann das Verarbeiten von Default-Annotationen eingeschaltet werden.
|writeId|false|Legt fest ob extra Attribute für IDs bei Arrays und Maps geschrieben werden sollen.
|encoding|UTF-8|Legt das Encoding fest.
|writeType|NONE|Legt 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.
|throwUnknown|true|Damit 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
|classLoader|XmlUnmarshaller.class.getClassLoader()|ClassLoader zum Laden von Klassen.
|attributeHandlers|Interne Handler|Handler welche die XML-Attribute verarbeiten, wie Namespace-Includes.
|pretty|true|Damit kann das Formattieren des generierten XML-Codes abgeschaltet werden.
|formatMap|Interne Formate|Registrierung von Klassen zur Formatierung von speziellen Objekten.
|namespaceStack|-|Zugriff auf den NamespaceStack der XML-Verarbeitung
|processIncludes|true|Damit können Schema-Include-Anweisungen komplett abgeschaltet werden.
|absoluteIncludes|true|Damit können absolute URLs bei Schema-Includes ausgeschaltet werden.
|throwDuplicate|true|Damit können die Exceptions bei konourrierenden Array- und Map-Einträgen abgeschaltet werden.
|usePrefix|false|Legt fest, ob die SOAP-Message ein Namespace-Prefix bekommen soll.
|processEnum|true|Legt fest, ob Enums generiert werden sollen.
|throwFault|true|Möglichkeit zum Abschalten von Exceptions bei SOAP-Faults.
|serializable|false|Damit können alle generierten Klassen serialisierbar gemacht werden.
|handleIds|true|Legt fest, ob ID-Angaben in Arrays und Maps verarbeitet werden.
|autoCdata|true|Legt fest, ob bei Bedarf automatisch CDATA-Elemente generiert werden sollen.
|logging|false|Legt fest, ob die XML-Kommunikation mitgeloggt werden soll.
|annotations|true|Legt fest ob Annotations generiert werden sollen.
|proxy|Proxy.NO_PROXY|Zu verwendendes java.net.Proxy
|connectTimeout|-|Timeout für die TCP-Connection.
|readTimeout|-|Timeout für das Lesen von der TCP-Connection.
|collectNamespace|true|Legt 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.
|unwrap|false|Auspacken von Response-Klassen. Kann zu Fehlern führen, wenn die SOAP-Response-Messages mehrere Elemente enthalten.
|transparentNtlm|true|Experimentelles Feature zum Unterbinden von transparenter NTLM-Authentifizierung, falls der Client auf Windows-Rechnern läuft.
|induceTypes|true|Legt fest, ob bei der Schema-Induktion Typen wie Integer oder Datum automatisch erkannt werden oder ob standardmäßig xsd:string verwendet wird.
|induceNillable|false|Steuert ob bei der Schema-Induktion Felder als nillable gekennzeichnet werden.
|valueInducer|IntegerInducer, UrlInducer, DateTimeInducer|Liste 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.
|minStrategy|LAX|Legt 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.
|maxStrategy|LAX|Legt 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.
|useStragegy|LAX|Legt 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.
|generateXsdElements|true|Legt 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.
|keepXmlElements|false|Legt 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.
|qualifyElements|true|Legt fest, ob untergeordnete XML-Elemente ebenfalls qualifiziert werden (also einen Namespace-Prefix erhalten).
|unqualifiedNamespace|true|Bei 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.
|qualifyAttributes|false|Legt fest, ob XML-Attribute qualifiziert werden (mit Namespace-Prefix versehen).
|saxParserFactory|System-Standard|Die verwendete Factory für den SAX-Parser kann hier festgelegt werden, ansonst wird der Standard von der JVM verwendet. Es kann erforderlich sein, diesen Parameter zu setzen, wenn Dritt-Code die Standard-Factory verstellt hat und [Abraxas] nicht mehr korrekt arbeitet.
|useAttributeNamespace|true|Steuert die Verarbeitung von Namespaces bei Attributen.
|wsdlStylesheet|null|Legt ein XSLT-Stylesheet fest, das generierten WSDL-Dokumenten als XSL-Instruction hinzugefügt wird. Damit ist es möglich, WSDL-Dokumente im Browser anzuzeigen. [Abraxas] Remote Service macht von diesem Feature gebrauch, um eine Browserdarstellung der Web-Services zu ermöglichzen.
|primitivesAsAttribute|true|Legt fest, ob Primitives (also String, Integer etc.) als Attribute geschrieben werden. Wird dieser Wert auf false gesetzt, werden alle Werte als eigene XML-Element-Tags geschrieben.
|root|null|Normaler Weise wird das Root-Element des Objektbaums beim Unmarshalling neu instantiiert, also mit new <Type>() erzeugt. Indem man das Root-Element in der XmlConfig setzt, erreicht man dass ein bestehendes Java-Objekt dafür verwendet wird. Das zugeführte XML wird praktisch auf einen bestehenden Objektbaum darüber geparst. Dieses Feature sollte mit Vorsicht benutzt werden, da es allerhand Komplikationen erzeugen kann.
|scanClassPath|false|Legt fest, ob der ClassPath nach XML-Klassen und Remote-Services gescannt wird. Dieser Wert macht nur im Zusammenhang mit scanConfigFiles Sinn, um den ClassPath-Scan durch ein Konfig-File zu aktivieren. Andernfalls startet man den ClassPath-Scan durch die Methode scanClassPath().
|scanPackage|List<String>|Eine Liste von Java-Packages, die gescannt werden soll. Werden hier ein oder mehrere Elemente angegeben, wird scanClassPath durchgeführt und braucht nicht mehr explizit angegeben werden.
|serviceFactory|com.intersult.xml.remote.ReflectiveServiceFactory|Die Service-Factory für das Erzeugen von Web-Services. Das Interface com.intersult.xml.remote.ServiceFactory kann selbst implementiert werden, wenn zum Beispiel Web-Services als Spring-Beans erzeugt werden sollen.
|superConfig|XmlConfig|Die Super-Konfiguration ist eine Instanz der XmlConfig, die verwendet wird um eine abraxas.xml auf eine bestehende XmlConfig zu parsen. SuperConfig deshalb, weil Abraxas seine XmlConfig mit sich selbst parst.
At line 98 changed one line
!Web Service (SOAP)
!!Element Handler
In der XmlConfig gibt es ein Property elementHandler. Üblicher Weise befindet sich darin der DefaultElementHandler. Element Handler sind dafür verantwortlich, die Hierarchie der Java-Objekte anhand der XML-Elemente zu handeln. Dazu gehört auch das Root-Element und Text-Contents.
Ein weiterer Handler der interessant sein kann, ist der RootElementHandler. Damit ist es möglich, ein XML auf einen bestehenden Object-Tree zu parsen. Normaler Weise wird dieser Handler durch ConfigXml.getRoot() und setRoot() angesprochen.
Damit ist es möglich, ein gegebenes XML auf einen bestehenden Objektbaum darauf zu parsen.
__Hinweis:__ Dies kann zur Verdoppelung von Collection- und Array-Elementen führen, da das Standardverhalten Elemente aus dem XML am Ende der Liste hinzufügt. Wenn man XML verwendet, das mit der Option config.setWriteId(true) geschrieben wurde, enthält dies die vorherigen Index-Werte. Dadurch erreicht man, dass die Elemente wieder an den gleichen Index geschrieben werden. Wurde die Collections in der Zwischenzeit allerdings verändert, werden jedoch überschrieben.
!!!Web Service (SOAP)
At line 114 changed one line
<artifactId>com.intersult.maven</artifactId>
<artifactId>abraxas-maven</artifactId>
At line 124 changed one line
<outputPath>${project.build.directory}/generated/weather</outputPath>
<outputPath>${project.build.directory}/generated-sources/weather</outputPath>
At line 149 changed 2 lines
!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).
!!!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).
At line 152 changed one line
Ein ganz einfaches Beispiel für die Anwendung von [Abraxas]:
Dies kann in der pom.xml dann so aussehen:
At line 155 changed 3 lines
String xml = "<java.lang.String>Test</java.lang.String>";
String string = (String)Xml.unmarshall(xml);
System.out.println(string);
<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>
At line 160 changed one line
__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).
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.
At line 162 changed one line
Umwandlung von Java-Objekten in XML:
!!Java-Generierung
Ein XML-Schema kann auch aus Java heraus generiert werden. Dies wird zum Beispiel implizit beim Verwenden von Web-Services durchgeführt:
At line 165 changed 3 lines
Foo foo = new Foo();
String xml = Xml.marshall(foo);
System.out.println(xml);
XsdWriter writer = new XsdWriter(new QName(
"http://intersult.com/test", XMLConstants.DEFAULT_NS_PREFIX, "test"));
writer.addElement(SomeClass.class);
writer.write(System.out);
At line 170 changed one line
__Erklärung:__ Das (beliebige) Objekt Foo wird in XML umgewandelt, indem es der Methode marshall der Klasse com.intersult.xml.Xml übergeben wird.
__Erklärung:__ Über Reflection wird aus der angegebenen Klasse ein Schema generiert. Als Reflection wird dabei [Intersult Coder|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.
* __MinStrategie:__ Evaluiert das Attribut minOccurs eines Schema-Elements und legt damit fest, wie oft ein XML-Element als Child-Element in einem Typ mindestens vorkommen muss. Default-Strategie ist LAX.
** __NONE:__ Es wird nie ein Auftreten von Elementen erzwungen.
** __LAX:__ Das Auftreten des Child-Elements wird erst dann erzwungen, wenn alle Parent-Elemente das Child-Element enthalten und mindestens zwei Fälle existieren. Die Strategie ist etwas entspannter als MEDIUM, da das Auftreten eines Falls nicht gewertet wird.
** __MEDIUM:__ Das Auftreten des Child-Elements wird erzwungen, sobald alle Parent-Elemente das Child-Element enthalten.
** __STRICT:__ Es wird die Mindestzahl von Child-Elementen verlangt, die im geringsten Fall festgestellt wurde.
* __MaxStrategie:__ Evaluiert das Attribut maxOccurs, ähnlich wie bei minOccurs. Hier ist zu beachten, dass die Schema-Validierung des XML-Parsers umgekehrt arbeitet. Die Default-Zahl von maxOccurs beträgt 1, das heißt es ist maximal 1 Child-Element erlaubt. Wird diese nicht ausdrücklich auf einen höheren Wert gesetzt, führt das Parsen des XML-Dokuments zu einem Fehler.
** __MEDIUM:__ Sobald ein Element mehrfach auftritt, wird maxOccurs auf "unbounded" gesetzt.
** __STRICT:__ Das Attribut maxOccurs wird auf genau den Integer-Wert gesetzt, wie das Child-Element maximal aufgetreten ist.
** __UNBOUNDED:__ Das maxOccurs wird immer auf "unbounded" gesetzt.
* __UseStrategy:__ Evaluiert das Attribut use beim Schema-Attribut "attribute". Es legt damit fest, ob ein Attribut optional ist oder erzwungen wird.
** __OPTIONAL:__ Alle Attribute sind optional.
** __LAX:__ Ein Attribut wird mit "required" markiert, wenn alle betreffenden Elemente das Attribut enthalten und mindestens zwei Elemente vorhanden sind.
** __STRICT:__ Ein Attribut wird mit "required" markiert, wenn alle betreffenden Elemente das Attribut enthalten, selbst dann wenn das Element nur einmal vorkommt.
!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:
||Parameter||Beschreibung
|resourceRoot|Ermö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>
|protocols|Ermöglicht das Einbinden globaler Protocols, wie bei generate-ws oder generate-schema.
|outputFile|Datei 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>
|xml|URL auf eine XML-Datei, aus der das Schema generiert werden soll.
|targetNamespace|Optional 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.
|prefix|Optional kann das zu verwendende Prefix angegeben werden. Andernfalls wird es aus der verarbeiteten XML-Datei entnommen.
|xmlConfig|Zugriff auf die XmlConfig, mit der Möglichkeit entsprechende Werte zu setzen. Insbesondere interessant sind hier die Strategie-Angaben zur Induktion von Anzahl und Typen.
|protocol|Optionale Angabe eines Protocols, das beim Zugriff auf die XML-Datei benutzt wird.
!!Maven Beispiel
Es liegt folgende XML-Datei als simple.xml vor:
{{{
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://intersult.com/test/simple">
<value>
<some-element>1</some-element>
<some-element>2</some-element>
</value>
<another-value>
<some-element>3</some-element>
<some-element>4</some-element>
<nested>
<value>
<another-element>1</another-element>
<another-element>2</another-element>
<another-element>3</another-element>
</value>
</nested>
</another-value>
</root>
}}}
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. Dies kann innerhalb der selben Instanz des Build-Plugins abraxas-maven geschehen:
{{{
<execution>
<id>generate-schema</id>
<goals>
<goal>generate-schema</goal>
</goals>
<configuration>
<schemas>
<schema>
<xsdPath>${project.build.directory}/generated-sources/simple</xsdPath>
<xsd>file:/${project.build.directory}/generated-resources/xsd/simple.xsd</xsd>
</schema>
</schemas>
</configuration>
</execution>
}}}
Die Klasse Root sieht dann wie folgt aus:
{{{
@Name("root")
@XmlNamespace(value = "http://intersult.com/test/simple", localPart = "root")
public class Root {
private Value value;
private AnotherValue anotherValue;
private URI xmlns;
public Value getValue() {
return value;
}
public void setValue(Value value) {
this.value = value;
}
@Name("another-value")
public AnotherValue getAnotherValue() {
return anotherValue;
}
public void setAnotherValue(AnotherValue anotherValue) {
this.anotherValue = anotherValue;
}
public URI getXmlns() {
return xmlns;
}
public void setXmlns(URI xmlns) {
this.xmlns = xmlns;
}
}
}}}
In einem Test kann nun die Datei simple.xml eingelesen werden:
{{{
@Test
public void testUnmarshal() {
XmlConfig config = new XmlConfig();
config.register(Root.class);
InputStream inputStream = getClass().getResourceAsStream("/simple.xml");
Root root = (Root)Xml.unmarshall(inputStream, config, "/simple.xml");
Assert.assertNotNull(root);
}
}}}
!!Java Beispiel
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);
}}}
!!!Schema Export
Es sind auch Fälle vorstellbar, in denen ein Schema für Klassen aus einem Projekt erzeugt werden soll. Dieses Schema kann dann als Bestandteil des Projekts während der Phase package in ein JAR oder WAR eingepackt werden. Dies kann den Anwender zum Beispiel im Anwenden des Content Assists beim Editieren von XML-Dateien unterstützen.
Der Eintrag in der pom.xml kann so aussehen:
{{{
<build>
<plugins>
<plugin>
<groupId>com.intersult</groupId>
<artifactId>abraxas-maven</artifactId>
<version>1.5-SNAPSHOT</version>
<executions>
<execution>
<id>export-schema</id>
<goals>
<goal>export-schema</goal>
</goals>
<configuration>
<resourceRoot>${project.build.directory}/generated-resources/export</resourceRoot>
<exports>
<export>
<targetNamespace>http://intersult.com/export</targetNamespace>
<prefix>ex</prefix>
<outputFile>${project.build.directory}/generated-resources/export/classes.xsd</outputFile>
<classes>
<class>com.intersult.test.ExportTest</class>
</classes>
</export>
</exports>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
}}}
!!!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 nur das RemoteServlet in der web.xml zu registrieren und eine META-INF/abraxas.xml anzulegen. Web-Services sind Java-Klassen, die mit der Annotation @XmlService versehen sind. Zusätzlich ist empfohlen, die Annotation @XmlNamespace zu verwenden, um den Target-Namespace des Service festzulegen.
Grundsätzlich bestehen zwei Möglichkeiten für das Registrieren von Mappings und Service-Klassen:
# Das manuelle Hinzufügen von Service-Klassen in der abraxas.xml mittels Mapping-Tag
# Das Aktivieren des ClassPath-Scanners in der abraxas.xml (Siehe [Abraxas#XML Config Files])
Das manuelle Eintragen erlaubt eine genauere Kontrolle, welche Klassen für das Mapping herangezogen werden:
{{{
<?xml version="1.0" encoding="UTF-8"?>
<xmlConfig xmlns="http://intersult.com/abraxas/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://intersult.com/abraxas/config http://intersult.com/xml/abraxas.xsd">
<mapping>com.intersult.abraxas.test.ExampleService</mapping>
<wsdlStylesheet>wsdl.xsl</wsdlStylesheet>
</xmlConfig>
}}}
!!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|Coder] 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>
<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:
{{{
@XmlService
@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.