Bei der Implementierung des Comet SOAP Service mit Axis stellen sich zwei Probleme:
2.1 Verwendung von multirefs ▲
Multirefs dürfen nicht verwendet werden.
Allerdings wird für Nachrichten, die Attachments enthalten (also getBinaryFile und putBinaryFile), zur Referenzierung von Attachments mit href="cid" auf einen sehr ähnlichen Mechanismus zurückgegriffen.
Die von Axis (WSDL2Java) standardmäßig generierten Typ-Klassen und -Serialisierer unterstützen dies nicht.
2.2 Nicht WS-I konforme WSDL ▲
getBinaryFile und
putBinaryFile transportieren Elemente, die
nicht über einen "Root-Part" beschrieben werden, als Attachment.
Laut
WS-I Attachment Profile 1.0 dürfen solche Elemente nicht als
<mime:content part="..." /> aufgeführt werden, wohl aber in multiPart-Nachrichten als Attachment übertragen werden. Axis bricht an dieser Stelle die Generierung von Skeleton-Code ab, andere Soap-Implementierungen (so auch die für den Comet-Client verwendete) sind hier toleranter.
Comet4Axis.wsdl ist die für Axis korrigierte WSDL-Beschreibung, die auf dieser Basis generierten Service-Klassen sind allerdings nicht vollständig.
2.3 Allgemeine Hinweise ▲
- Die vorliegende Lösung basiert weitgehend auf den mit WSDL2Java generierten Java-Klassen.
Minimale Anpassungen waren in CometBinary notwendig, angepasste Serialisierer / Deserialisierer für die Typen CometBinary und Binary wurden im Paket de.werk_ii.soap.comet.ser hinzugefügt.
Zur Integration des Service muß "nur noch" in CometImpl die Schnittstelle zum jeweiligen System implementiert werden (z.Zt. werden hier alle Operationsaufrufe an einen Testservice delegiert), alles andere regeln die mitgelieferten Klassen.
-
Zweck dieser Dokumentation und des mitgelieferten Quellcodes ist es, die SOAP-Anbindung des Comet Plugins zu erleichtern.
Zu diesem Zweck wird Dokumentation und Quellcode Partnern von Werk II kostenlos zur Verfügung gestellt. Jede andere Verwendung oder Weitergabe bedarf der ausdrücklichen Genehmigung der Werk II GmbH.
- Die vorgestellte Lösung ist nicht perfekt! Einige Bugs sind bekannt (siehe Release Notes), die Versionsnummer (0.2.2) kann als weiterer Hinweis hierfür verstanden werden. Korrekturen, Ergänzungen und Verbesserungsvorschläge nehmen wir gerne entgegen (mailto:christoph.soergel@werk-ii.de) und arbeiten sie in kommende Versionen ein.
de.werk_ii.soap.comet.Schema.CometBinary:
- Ändern des Typs von
content nach de.werk_ii.soap.comet.Schema.Binary
- entsprechend Anpassung im Konstruktor
// ...
try {
byte[] filecontent = (byte[])content;
this.content = new de.werk_ii.soap.comet.Schema.Binary(filename, mimetype, filecontent);
}
catch (java.lang.ClassCastException e) {
this.content = null;
}
(der Datei-Inhalt soll als byte-Array übergeben werden, denkbar wäre natürlich jeder andere Typ, der sich in byte[] umwandeln läßt, Signatur des Konstruktors wurde nicht geändert, auch das wäre möglich).
- Ändern des Rückgabetyps von
getContent und Parametertyp von setContent jeweils nach de.werk_ii.soap.comet.Schema.Binary.
- Rückgabe eines eigenen Deserialisierers in
getDeserializer
Neu sind die Klassen
de.werk_ii.soap.comet.Schema.Binary,
de.werk_ii.soap.comet.ser.BinarySerializer und
de.werk_ii.soap.comet.ser.BinarySerializerFactory.
Binary dient vor allem dazu, durch die Verwendung einer eigenen Klasse für den Inhalt des Elements
content einen eigenen Serialisierer definieren zu können.
Der eigentlich interessante Teil (auch nur eine Handvoll Zeilen...) passiert in
BinarySerializer::serialize:
// ...
// Wir wollen nur Binary's
if (!(value instanceof de.werk_ii.soap.comet.Schema.Binary))
throw new IOException(Messages.getMessage("cantSerialize01"));
// Shorthand fuer Parameter "value" ... erspart einige Typecasts weiter unten
de.werk_ii.soap.comet.Schema.Binary binary = (de.werk_ii.soap.comet.Schema.Binary)value;
// Attachment generieren und hinzufuegen
org.apache.axis.Message message = context.getCurrentMessage();
org.apache.axis.attachments.AttachmentPart part =
new org.apache.axis.attachments.AttachmentPart(
new javax.activation.DataHandler( new MemoryOnlyDataSource(binary.getContent()) ));
message.addAttachmentPart(part);
// Content-Id des Attachments ermitteln
java.lang.String cid = part.getContentId();
// "href" Attribut generieren und hinzufuegen
AttributesImpl attribs;
if (attributes == null) {
attribs = new AttributesImpl();
} else if (attributes instanceof AttributesImpl) {
attribs = (AttributesImpl)attributes;
} else {
attribs = new AttributesImpl(attributes);
}
attribs.addAttribute("", "href", "", "", "cid:" + cid);
// Element schreiben
context.startElement(name, attribs);
context.endElement();
// ...
(mit den Kommentaren einigermaßen selbsterklärend)
Neu in Version 0.2 sind Deserialisierer für den Typ CometBinary. Hier wird umgekehrt das über
href referenzierte Attachment in ein Objekt der Klasse
de.werk_ii.soap.comet.Schema.Binary umgewandelt (die Implementierung kann in einigen Punkten sicherlich verbessert werden, das Prinzip funktioniert aber).
CometBinaryDeserializer::onStartChild:
// ...
if (localName.equals("content")) {
parseAttachment(attributes, context);
}
// ...
CometBinaryDeserializer::parseAttachment:
// ...
// CometBinary result = (CometBinary)getValue();
try {
String href = ((AttributesImpl)attributes).getValue("href");
if (href.regionMatches(0, "cid:", 0, 4)) {
href = href.substring(4);
}
Message message = context.getMessageContext().getCurrentMessage();
Attachments attachments = message.getAttachmentsImpl();
IncomingAttachmentStreams streams = attachments.getIncomingAttachmentStreams();
IncomingAttachmentStreams.IncomingAttachmentInputStream current;
byte[] content = null;
byte[] buffer = new byte[CometBinaryDeserializer.MIN_BUFFER_SIZE];
while((current = streams.getNextStream()) != null) {
String cid = null;
if ((cid = current.getContentId()) == null) {
// auch das soll es geben...
cid = current.getHeader("Content-ID");
}
if (cid != null) {
cid = cid.substring(1, cid.length() - 1);
if (cid.equals(href)) {
content = (new ByteBuffer(result.getSize())).read(current);
break;
}
}
// Rest auslesen und "wegwerfen"
while(!streams.isReadyToGetNextStream()) {
current.read(buffer, 0, CometBinaryDeserializer.MIN_BUFFER_SIZE);
}
}
result.setContent(new de.werk_ii.soap.comet.Schema.Binary(result.getFilename(),
result.getMimetype(),
content));
}
catch (Exception e) {
throw new SAXException(e);
}
// ...