SOAP-Schnittstelle

Bei SOAP findet die Kommunikation mit Nachrichten im XML-Format statt. Die Übertragung von Dateien, die konvertiert werden sollen oder als Ergebnis an den Client zurückgesendet werden, ist als MTOM-Attachment realisiert.

Abbildung 7.1. Schematischer Aufbau der SOAP-Schnittstelle

Die SOAP-Schnittstelle bereitet SOAP-Anfrage auf, führt die Verarbeitung durch und sendet das Ergebnis als SOAP-Antwort zurück.

Tipp

Zur Entwicklung und zum Debugging von SOAP-Anfragen empfehlen wir soapUI.

Aufbau einer SOAP-Nachricht

Nachdem wie in Abschnitt „Konfiguration SOAP-Schnittstelle“ beschrieben die Webservice-Schnittelle aktiviert wurde, kann unter http://<url>?wsdl (z. B. bei Konfiguration wie im o. g. Kapitel: http://localhost:9000/jadiceServer?wsdl) die formale Beschreibung der Schnittstelle im WSDL-Format heruntergeladen werden. Damit kann in vielen Webservice-Frameworks Code generiert werden, um den Webservice des jadice servers anzusprechen. Bei einer SOAP-Anfrage kann jadice server auf zwei unterschiedliche Arten angesprochen werden:

  • Der Workflow wird anhand eines Templates, das zuvor serverseitig abgelegt wurde, vorkonfiguriert

  • Der Workflow wird zur Laufzeit innerhalb der SOAP-Anfrage definiert.

Die beiden Möglichkeiten werden in den beiden folgenden Kapiteln dargestellt.

Anfrage anhand eines Templates

Es ist möglich, serverseitig eine in XML kodierte Jobbeschreibung abzulegen, sodass ein Client bei einer SOAP-Anfrage auf diese verweisen kann und der Job nicht zur Laufzeit konfiguriert werden muss.

Der Aufbau dieser Nachricht soll anhand des folgenden Beispiels erläutert werden:

Beispiel 7.1. Beispiel einer SOAP-Anfrage mit Job-Template

  <soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  	xmlns:ws="http://ws.server.jadice.levigo.com/">
   <soapenv:Header/>
   <soapenv:Body>
    <ws:run>
     <ws:job templateLocation="resource:/jobtemplates/x2pdf.xml">
      <ws:property name="dp.rulesetName">default</ws:property>
      <ws:property name="dp.targetMimeType">application/pdf</ws:property>
      <ws:property name="dp.timeout">8000</ws:property>
      <ws:stream mimeType="unknown/*" uuid="123456789" nodeId="node0">
       <ws:documentData>BASE64_encoded_data</ws:documentData>
      </ws:stream>
     </ws:job>
    </ws:run>
   </soapenv:Body>
  </soapenv:Envelope>
          


Neben den vom SOAP-Standard vorgegebenen Elementen für Header und Body gibt es das spezifische Element run, das die vom Webservice angebotene Methode run adressiert.

Darin wird ein Job (siehe Kapitel 4, Systemarchitektur) definiert, der über ein Template vordefiniert ist (siehe Abschnitt „Definition von Job-Templates“). Im Attribut templateLocation steht dabei der Ort, an dem das jeweilige Template serverseitig zu finden ist. Sind im Template Variablen definiert, so können diese mittels property-Elementen konfiguriert bzw. deren Standardwert überschrieben werden. Optional ist das Attribut messageID. Dies kann der Client frei vergeben und wird ggf. in einer Antwort des Servers übernommen.

Zu verarbeitende Datenströme werden über stream-Elemente in der SOAP-Nachricht referenziert. Die Angabe einer eindeutigen ID (uuid) und des MIME-Typen sind optional. Sollte der MIME-Type nicht bekannt sein, aber dennoch angegeben werden, so ist dort unknown/* anzugeben.

Sind in der Templatedatei mehrere StreamInputNodes definiert, so muss eine eindeutige Zuordnung getroffen werden, welcher Datenstrom an welchen StreamInputNode gesendet wird. Dies erfolgt durch das Attribut nodeId. Dies verweist auf die ID, die dem StreamInputNode innerhalb des Templates (Attribut id) gegeben wurde.

Die eigentlichen Daten können entweder direkt im Tag documentData als Base64-codierter Datenstrom angegeben werden oder in einem multipart/related-Container referenziert werden, der die hier angegebene CID (contentID) besitzt, wenn das MTOM-Feature aktiviert ist.

Jobdefinition innerhalb der SOAP-Nachricht

Soll der Client keine serverseitig vordefinierte Jobkonfiguration verwenden, ist es möglich, diese in der SOAP-Anfrage einzubetten. Das Format ist hierbei das selbe, wie innerhalb eines separaten Job-Templates (vgl. Abschnitt „Definition von Job-Templates“). Anstelle des Root-Elements job wird hierbei die Definition als configuration-Element in die SOAP-Anfrage eingebettet.

Das folgende Beispiel soll dies veranschaulichen:

Beispiel 7.2. Beispiel einer SOAP-Anfrage mit eingebetteter Job-Definition

  <soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  	xmlns:ws="http://ws.server.jadice.levigo.com/">
   <soapenv:Header/>
   <soapenv:Body>
    <ws:run>
     <ws:job messageID="4711">
      <ws:configuration type="demultipexer example">
       <ws:nodes>
        <ws:node class="com.levigo.jadice.server.nodes.StreamInputNode" id="input1" />
        <ws:node class="com.levigo.jadice.server.nodes.StreamInputNode" id="input2" />
        <ws:node class="com.levigo.jadice.server.nodes.DemultiplexerNode" id="demux" />
        <ws:node class="com.levigo.jadice.server.nodes.StreamOutputNode" id="out" />
       </ws:nodes>
       <ws:connections>
        <ws:connect from="input1" to="demux" />
        <ws:connect from="input2" to="demux" />
        <ws:connect from="demux" to="out" />
       </ws:connections>
      </ws:configuration>
      <ws:stream nodeId="input1">
       <ws:documentData>BASE64_encoded_data</ws:documentData>
      </ws:stream>
      <ws:stream nodeId="input2">
       <ws:documentData>BASE64_encoded_data</ws:documentData>
      </ws:stream>
     </ws:job>
   </ws:run>
  </soapenv:Body>
  </soapenv:Envelope>
          


In diesem Beispiel werden zwei StreamInputNodes miteinander über einen DemultiplexerNode gekoppelt und die Eingabedaten unverändert an den Client zurückgesendet.

Die Definition der Nodes und welchen Workflow-Graphen sie bilden, ist innerhalb des Blocks configuration beschrieben.

Hier ist außerdem zu sehen, wie es möglich ist, bestimmte Eingabeströme an einen StreamInputNode zu binden: Das erste Dokument wird an den ersten (nodeId input1), das zweite Dokument an den zweiten StreamInputNode (nodeId input2) gebunden.

Aufbau einer SOAP-Antwort

Der Aufbau einer Antwort, die dem Client als Antwort auf eine Anfrage geschickt wird, ist auch in der oben genannten WSDL spezifiziert.

Eine mögliche Antwort könnte so aussehen:

Beispiel 7.3. Beispiel einer SOAP-Antwort

  <<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
    <js:runResponse xmlns:js="http://ws.server.jadice.levigo.com/">
     <js:return>
      <js:stream>
       <js:documentData>BASE64_encoded_data</js:documentData>
      </js:stream>
      <js:status>COMPLETED</js:status>
     </js:return>
    </js:runResponse>
   </soap:Body>
  </soap:Envelope>
          


Neben einer (eventuell leeren) Menge von Ergebnisströmen, die jeweils über eine eindeutige ID in einem multipart/related-Container referenziert werden, ist dort eine Status-Meldung vorhanden. Folgende Werte sind möglich:

Wert Bedeutung

COMPLETED

Job wurde durchgeführt.

FAILED

Job konnte nicht durchgeführt werden.

In beiden Fällen kann das return-Element eine Menge an log-entry-Elementen enthalten, die Hinweise über den Fehlschlag der Verarbeitung oder Meldungen, die während der Verarbeitung aufgetreten sind, enthalten (vgl. Abschnitt „Erstellung eines JobListeners“). Die Fehlermeldung, die gezeigt wird, wenn eine nicht vorhandene Jobtemplate-Datei referenziert wird, zeigt das folgende Beispiel:

Beispiel 7.4. Beispiel einer SOAP-Antwort mit Fehlermeldung

  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
     <js:runResponse xmlns:js="http://ws.server.jadice.levigo.com/">
      <js:return messageID="12345">
       <js:log-entry timeStamp="31.12.2009 22:33:44">
        <js:level>ERROR</js:level>
        <js:id>JS.WEBSERVICE-EXCEPTION</js:id>
        <js:message>java.io.FileNotFoundException: Could not locate resource: does_not_exist.xml</js:message>
       </js:log-entry>
      <js:status>FAILED</js:status>
     </js:return>
    </js:runResponse>
   </soap:Body>
  </soap:Envelope>
          


Definition von Job-Templates

Durch die Definition von Job-Templates wurde die Möglichkeit geschaffen, dass Clients nun nicht mehr die internen Schritte des jadice server, die für eine Konvertierung notwendig sind, kennen müssen. Diese werden für die Webservice-Schnittstelle an zentraler Stelle vorgehalten werden. Dadurch muss der Client nunmehr nur noch die Webservice-Methode run sowie den Ort des auszuführenden Templates kennen (diese liegen i.d.R. im Unterordner /server-config/).

Im Ordner /server-config/jobtemplates liegt die XSD-Definition für diese Templates.

Ein Beispiel, wie solch ein Template aussehen kann, ist das im Lieferumfang enthaltene Template x2pdf.xml, das ähnlich dem Beispiel aus Abschnitt „Konvertierung unbekannter Eingabedaten in ein einheitliches Format (PDF)“ unbekannte Eingabedaten identifiziert und in das PDF-Format umwandelt:

Beispiel 7.5. Beispiel eines Job-Templates

  <job xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="jobtemplate.xsd" type="x2pdf">
   <ws:properties>
    <ws:property name="PROPERTY_KEY_COMMUNICATION_TIMEOUT">${communication_timeout:22000}</ws:property>
   </ws:properties>
   <ws:nodes>
    <ws:node class="com.levigo.jadice.server.nodes.StreamInputNode" id="node0">
     <ws:property name="timeout">${timeout:6000}</ws:property>
    </ws:node>
    <ws:node class="com.levigo.jadice.server.nodes.DynamicPipelineNode" id="node1">
     <ws:property name="ruleset">${ruleset:resource:/dynamic-pipeline-rules/default.xml}</ws:property>
     <ws:property name="targetMimeType">${targetMimeType:application/pdf}</ws:property>
     <ws:property name="timeout">${timeout:6000}</ws:property>
    </ws:node>
    <ws:node class="com.levigo.jadice.server.nodes.StreamOutputNode" id="node2">
     <ws:property name="timeout">${timeout:6000}</ws:property>
    </ws:node>
   </ws:nodes>
   <ws:connections>
    <ws:connect from="node0" to="node1" />
    <ws:connect from="node1" to="node2" />
   </ws:connections>
  </ws:job>
          


Wie man erkennen kann, ist ein Template in drei Blöcke gegliedert:

  • Eigenschaften, die den kompletten Job betreffen (Timeouts etc.)

  • Definition von einzelnen Nodes (Element node) und deren Eigenschaften

  • Verkettung der Nodes zu einem Workflow. Dieser muss einen azyklischen Graph bilden.

Sofern die einzelnen Nodes und deren Eigenschaften den Konventionen für java beans entsprechen, können die jeweiligen Eigenschaften hier einfach über deren Namen gesetzt werden. Außerdem ist es möglich, diese variabel zu machen, wie im obigen Beispiel zu sehen ist. Dies geschieht nach folgendem Muster: ${<Bezeichner>} oder ${<Bezeichner>:<Standardwert>}, wobei der Bezeichner zwingend mit einem Buchstaben beginnen muss und als weitere Zeichen Buchstaben, Ziffern, _ (Unterstrich), - (Bindestrich) und . (Punkt) haben darf.

Durch diesen Bezeichner können diese Werte beim SOAP-Aufruf als Eigenschaft (Element <property name="bezeichner">wert</property>) gesetzt bzw. überschrieben werden. Werden Variablen, die keinen Standardwert haben, nicht gesetzt, so führt dies beim Aufruf zu folgendem Fehler: com.thoughtworks.xstream.converters.ConversionException: Pattern refers to undefined variable <Bezeichner> for which there is no default

Die einzelnen Node-Elemente müssen eine für das jeweilige Template eindeutige ID einhalten. Durch diese werden sie im connections-Block zu einem Workflow verkettet.

Sollen über den SOAP-Aufruf, der zu diesem Template gehört, Daten vom Client zum Server übertragen werden, ist es notwendig, mindestens einen StreamInputNode zu definieren. Werden mehrere StreamInputNodes definiert, so müssen die einzelnen stream-Elemente in der SOAP-Anfrage den jeweils zutreffenden Node über das Attribut nodeId referenzieren. Dies entfällt, wenn es genau einen StreamInputNode gibt.

Die Datenströme, die aus StreamOutputNodes resultieren, werden als MTOM-Attachments in der SOAP-Antwort an den Client zurückgesendet. Dabei ist es auch möglich, mehrere StreamOutputNodes zu definieren. Dabei ist die Reihenfolge, in der die StreamOutputNodes abgefragt werden, um ihre Datenströme an die SOAP-Antwort anzuhängen, zufällig.

Um Job-Templates in eine SOAP-Anfrage einzubetten (siehe Abschnitt „Jobdefinition innerhalb der SOAP-Nachricht“), muss das Root-Element job entfernt werden; der Inhalt wird stattdessen an das Element configuration in der SOAP-Anfrage eingehängt. Das XML-Attribut type wird verwendet, um den Job-Typen so im Java-Code in Beispiel 6.2, „Job konfigurieren und ausführen“ zu deklarieren.

Generierung von Webservice-Clients

Da die Webservice-Schnittstelle durch die WSDL klar definiert ist, können frei verfügbare Webservice-Bibliotheken diese Definition verarbeiten und daraus Proxy-Klassen generieren, die die notwendigen SOAP-Anfragen kapseln und dadurch eine effiziente Entwicklung von Clientanwendungen ermöglichen. In diesem Kapitel wird dies anhand Suns Referenzimplemenentierung von JAX-WS, der Bibliothek Apache Axis2 und der Bibliothek Apache CXFgezeigt.

JAX-WS Referenzimplemenentierung

Im Lieferumfang des Java Development Kit (JDK) Version 1.7 befindet sich das Kommandozeilentool wsimport, mit dem Proxy-Klassen generiert werden können. Ist der Webservice von jadice server wie in Abschnitt „Konfiguration SOAP-Schnittstelle“ beschrieben aktiviert worden, werden mit folgendem Aufruf die notwenden Clientklassen erzeugt:

          <jdk1.7>\bin\wsimport http://localhost:9000/jadiceServer?wsdl -keep -extension 
        

Der Parameter -keep bewirkt, dass nicht nur die Klassen, sondern auch deren Quelltexte gespeichert werden. Zur weiteren Entwicklung empfehlt es sich, mit diesen weiter zu arbeiten. Abbildung 7.2, „Von JAX-WS generierte Klassen“ zeigt die generierten Klassen, die in die Entwicklungsumgebung eingebunden werden können.

Abbildung 7.2. Von JAX-WS generierte Klassen

JAX-WS erzeugt aus der WSDL mehrere Klassen, die direkt in der Entwicklungsumgebung eingebunden werden können.

Einstiegspunkte für eine Clientanwendung sind die Klassen JadiceServerJobInvoker und JobInvocationService, über die ein Zugriff auf die SOAP-Schnittstelle erfolgt, und die Klasse JobConfiguration über die der Aufruf konfiguriert wird. Eine minimale Implementierung kann so aussehen:

Beispiel 7.6. Implementierung eines SOAP-Clients mit der JAX-WS Referenzimplemenentierung

// Abstraction of the SOAP interface:
JadiceServerJobInvoker invoker = new JadiceServerJobInvoker();
JobInvocationService service = invoker.getJobInvocationServiceImplPort();

// Configuration of the job
JobConfiguration job = new JobConfiguration();
job.setTemplateLocation("resource:/jobtemplates/x2pdf.xml");

// Optional: Apply a property (e.g. a timeout)
Property timeout = new Property();
timeout.setName("timeout");
timeout.setValue("20000");
job.getProperty().add(timeout);

// Attach input data (if the template defines a StreamInputNode)
Stream inputStream = new Stream();
inputStream.setDocumentData(…);
job.getStream().add(inputStream);

// Submit the SOAP request (method is blocking!)
JobResult result = service.run(job);

// Retrieve result status
ResultStatus status = result.getStatus();

for (Log log : result.getLogEntry()) {
  // Evaluate log entries...
}

// Retrieve and handle result data
for (Stream stream : result.getStream()) {
  InputStream is = stream.getDocumentData().getInputStream();
  …      
}


Wenn die Security-Schnittstelle von jadice server wie in Abschnitt „Konfiguration Security-Schnittstelle“ beschrieben aktiviert ist, muss der clientseitige SOAP-Request um einen Security-Header erweitert werden. Folgendes Beispiel zeigt dies anhand der Java-WS Referenzimplementierung. Ein SOAPHandler erzeugt den benötigeten Security-Header:

Beispiel 7.7. Implementierung eines SecuritySoapHeaderHandler

public class SecurityHeaderSoapHandler implements SOAPHandler<SOAPMessageContext> {

  private static final String SOAP_HEADER_SECURITY_NAMESPACE //
    = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
  private static final String SOAP_HEADER_USER_TOKEN_NAMESPACE //
    = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
  private static final String SOAP_HEADER_PASSWORD_TYPE_NAMESPACE //
    = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";

  @Override
  public boolean handleMessage(SOAPMessageContext context) {
    final Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    if (outboundProperty.booleanValue()) {

      final SOAPMessage message = context.getMessage();
      try {

        SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
        SOAPHeader header = envelope.getHeader();

        SOAPElement security = header.addChildElement("Security", "wsse", SOAP_HEADER_SECURITY_NAMESPACE);

        SOAPElement usernameToken = security.addChildElement("UsernameToken", "wsse");
        usernameToken.addAttribute(new QName("xmlns:wsu"), SOAP_HEADER_USER_TOKEN_NAMESPACE);

        SOAPElement username = usernameToken.addChildElement("Username", "wsse");
        username.addTextNode("myUsername");

        SOAPElement password = usernameToken.addChildElement("Password", "wsse");
        password.setAttribute("Type", SOAP_HEADER_PASSWORD_TYPE_NAMESPACE);
        password.addTextNode("myPassword");
      } catch (Exception e) {
        // Causes the JAX-WS runtime to cease handler processing and generate a fault
        throw new RuntimeException("Cannot apply username token", e);
      }
    }
    return outboundProperty;
  }


Dieser Handler wird mittels eines HandlerResolvers dem Invoker gesetzt. Folgender Abschnitt des Client-Codes muss hierzu angepasst werden:

Beispiel 7.8. Implementierung eines Security Headers mit JAX-WS Referenzimplementierung

// Abstraction of the SOAP interface:
JadiceServerJobInvoker invoker = new JadiceServerJobInvoker();
invoker.setHandlerResolver(new HandlerResolver() {

  @Override
  public List<Handler> getHandlerChain(PortInfo portInfo) {
    List<Handler> handlerChain = new ArrayList<Handler>();

    SecurityHeaderSoapHandler securtiyHandler = new SecurityHeaderSoapHandler();

    handlerChain.add(securtiyHandler);

    return handlerChain;
  }
});
JobInvocationService service = invoker.getJobInvocationServiceImplPort();


Apache Axis2

Apache Axis2 ist unter http://axis.apache.org/axis2/java/core/ verfügbar und steht unter der Apache License. Ist der Webservice von jadice server wie in Abschnitt „Konfiguration SOAP-Schnittstelle“ beschrieben aktiviert worden, werden mit folgendem Aufruf die notwendigen Clientklassen erzeugt:

            <AXIS2_HOME>\bin\wsdl2java
  	        -o generatedCode
  	        -p com.levigo.jadice.server.ws.client.axis2.stub 
  	        -d jaxbri 
  	        -uri http://localhost:9000/jadiceServer?wsdl
        

Die Verwendung der Parameter -o für das Ausgabeverzeichnis und -p für den zu verwendenden Paketnamen sind optional. Der Parameter -d bestimmt, welches Databinding für die Wandlung nach / von XML verwendet werden soll. Standard ist das Apache Axis Data Binding (ADB). Dieses hat in der aktuellen Fassung jedoch Probleme mit der Deserialisierung von SOAP/MTOM-Attachments, sodass stattdessen auf die JAX-B Referenzimplemenentierung (jaxbri) zurückgegriffen werden sollte.

Einstiegspunkte für eine Clientanwendung sind die Klassen JadiceServerJobInvokerStub und Run, über die ein Zugriff auf die SOAP-Schnittstelle erfolgt, und die Klasse JobConfiguration, über die der Aufruf konfiguriert wird. Eine minimale Implementierung kann so aussehen:

Beispiel 7.9. Implementierung eines SOAP-Clients mit Apache Axis2

// Abstraction of the SOAP interface:
JadiceServerJobInvokerStub invoker = new JadiceServerJobInvokerStub();

// Configuration of the job
JobConfiguration job = new JobConfiguration();
job.setTemplateLocation("resource:/jobtemplates/x2pdf.xml");

// Optional: Apply a property (e.g. a timeout)
Property timeout = new Property();
timeout.setName("timeout");
timeout.setValue("20000");
job.getProperty().add(timeout);

// Attach input data (if the template defines a StreamInputNode)
Stream inputStream = new Stream();
inputStream.setDocumentData(…);
job.getStream().add(inputStream);

// Configure the SOAP request
Run run = new Run();
run.setJob(job);

// Submit the SOAP request (method is blocking!)
RunResponse response = invoker.run(run);

// Retrieve the result object
JobResult result = response.getReturn();

// Retrieve result status
ResultStatus status = result.getStatus();

for (Log log : result.getLogEntry()) {
  // Evaluate log entries...
}

// Retrieve and handle result data
for (Stream stream : result.getStream()) {
  InputStream is = stream.getDocumentData().getInputStream();
  …      
}


Wenn die Security-Schnittstelle von jadice server wie in Abschnitt „Konfiguration Security-Schnittstelle“ beschrieben aktiviert ist, muss der clientseitige SOAP-Request um einen Security-Header erweitert werden. Folgendes Beispiel zeigt dies für Apache Axis2.

Beispiel 7.10. Erzeugung eines Security Headers

private static final String SOAP_HEADER_SECURITY_NAMESPACE = //
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
private static final String SOAP_HEADER_USER_TOKEN_NAMESPACE = //
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
private static final String SOAP_HEADER_PASSWORD_TYPE_NAMESPACE = //
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";

private static OMElement createSecurityHeader() {
  OMFactory omFactory = OMAbstractFactory.getOMFactory();
  OMElement omSecurityElement = omFactory.createOMElement(new QName(SOAP_HEADER_SECURITY_NAMESPACE, "Security", "wsse"));
  omSecurityElement.addAttribute("mustUnderstand", "1", null);
  OMElement omUsertoken = omFactory.createOMElement(new QName(SOAP_HEADER_SECURITY_NAMESPACE, "UsernameToken", "wsse"));
  
  omUsertoken.declareNamespace(SOAP_HEADER_USER_TOKEN_NAMESPACE, "wsu");
  
  OMElement omUserName = omFactory.createOMElement(new QName(SOAP_HEADER_SECURITY_NAMESPACE, "Username", "wsse"));
  omUserName.setText("myUsername");
  
  OMElement omPassword = omFactory.createOMElement(new QName(SOAP_HEADER_SECURITY_NAMESPACE, "Password", "wsse"));
  omPassword.addAttribute("Type", SOAP_HEADER_PASSWORD_TYPE_NAMESPACE, null);
  omPassword.setText("myPassword");
  
  omUsertoken.addChild(omUserName);
  omUsertoken.addChild(omPassword);
  
  omSecurityElement.addChild(omUsertoken);
     
  return omSecurityElement;
}


Der erzeugte Header wird mit den folgenden Anpassungen des Webservice Client-Codes gesetzt und für jeden Webservice-Request verwendet:

Beispiel 7.11. Implementierung eines Security Headers mit Apache Axis2

// Abstraction of the SOAP interface:
JadiceServerJobInvokerStub invoker = new JadiceServerJobInvokerStub();
ServiceClient _stub = invoker._getServiceClient();
_stub.addHeader(createSecurityHeader());


Apache CXF

Apache CXF ist unter http://cxf.apache.org/ verfügbar und steht unter der Apache License. Ist der Webservice von jadice server wie in Abschnitt „Konfiguration SOAP-Schnittstelle“ beschrieben aktiviert worden, werden mit folgendem Aufruf die notwendigen Clientklassen erzeugt:

            <CXF_HOME>\bin\wsdl2java
  	        -d generatedCode
  	        -p com.levigo.jadice.server.ws.client.cxf 
  	        http://localhost:9000/jadiceServer?wsdl
          

Die Verwendung der Parameter -d für das Ausgabeverzeichnis und -p für den zu verwendenden Paketnamen sind optional.

Einstiegspunkte für eine Clientanwendung sind die Klassen JadiceServerJobInvoker und Run, über die ein Zugriff auf die SOAP-Schnittstelle erfolgt, und die Klasse JobConfiguration, über die der Aufruf konfiguriert wird. Eine minimale Implementierung kann so aussehen:

Beispiel 7.12. Implementierung eines SOAP-Clients mit Apache CXF

// Abstraction of the SOAP interface:
JadiceServerJobInvoker invoker = new JadiceServerJobInvoker();
JobInvocationService service = invoker.getJobInvocationServiceImplPort();

// Configuration of the job
JobConfiguration job = new JobConfiguration();
job.setTemplateLocation("resource:/jobtemplates/x2pdf.xml");

// Optional: Apply a property (e.g. a timeout)
Property timeout = new Property();
timeout.setName("timeout");
timeout.setValue("20000");
job.getProperty().add(timeout);

// Attach input data (if the template defines a StreamInputNode)
Stream inputStream = new Stream();
inputStream.setDocumentData(…);
job.getStream().add(inputStream);

Run run = new Run();
run.setJob(job);

// Submit the SOAP request (method is blocking!)
RunResponse response = service.run(run);

// Retrieve the result object
JobResult result = response.getReturn();

// Retrieve result status
ResultStatus status = result.getStatus();

for (Log log : result.getLogEntry()) {
  // Evaluate log entries...
}

// Retrieve and handle result data
for (Stream stream : result.getStream()) {
  InputStream is = stream.getDocumentData().getInputStream();
  …      
}


Wenn die Security-Schnittstelle von jadice server wie in Abschnitt „Konfiguration Security-Schnittstelle“ beschrieben aktiviert ist, muss der clientseitige SOAP-Request um einen Security-Header erweitert werden. Hierbei kann die gleiche Syntax wie bei der JAX-WS Referenzimplementierung verwendet werden, siehe „JAX-WS Referenzimplemenentierung“

[jadice server Version 5.8.7.0: Dokumentation für Entwickler und Administratoren. Veröffentlicht: 2021-04-15]