Comet SOAP 1.0 und Axis
Inhalt
1. Voraussetzungen
2. Motivation
2.1 Verwendung von multirefs
2.2 Nicht WS-I konforme WSDL
2.3 Allgemeine Hinweise
3. Deployment / Installation
4. Anpassungen / Erweiterungen
5. Release Notes

Version dieser Dokumentation und der mitgelieferten Klassen: 0.4 RC 1, Datum: 23.06.2006. Einige Punkte sind noch offen, siehe Release Notes.
© 2003 - 2023 WERK II GmbH, Philosophenweg 51, D-47051 Duisburg, http://www.priint.com
1. Voraussetzungen  
Generiert und getestet wurde der AXIS Comet Service mit folgenden Komponenten Bei Verwendung der Service Test Implementierung (de.werk_ii.soap.comet.test.CometTestImpl):

2. Motivation  
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.

3. Deployment / Installation  
Zur Einrichtung des Service wurden folgende Schritte durchgeführt (bei besserer Kenntnis der Axis Tools etc. wären möglicherweise elegantere Varianten denkbar...):

4. Anpassungen / Erweiterungen  
de.werk_ii.soap.comet.Schema.CometBinary:
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);
            }
            // ...

5. Release Notes  
TODO:

© 2003 - 2023 WERK II GmbH, Philosophenweg 51, D-47051 Duisburg, http://www.werk-ii.de