SOAP Interface

In SOAP the communication is realized via messages in the XML format. Files which are to be converted or sent back to the client as a result are transferred as MTOM-Attachment.

Figure 7.1. Layout of the SOAP Interface

The SOAP Interface receives the SOAP request, processes the request and sends the result back as a SOAP reply.

Tip

We recommend soapUI for developing and debugging SOAP requests.

Structure of a SOAP Message

After activating the webservice interface as described in the section called “Configuring the SOAP Interface”, the formal description of the interface can be downloaded in WSDL format at http://<url>?wsdl (for example, when configured as described in the section called “Configuring the SOAP Interface”,: http://localhost:9000/jadiceServer?wsdl). This enables you to generate code in many webservice frameworks in order to communicate with the webservice of jadice server. jadice server can be addressed in two different ways with a SOAP request:

  • The workflow is pre-configured with the help of a template that has been previously stored on the server.

  • The workflow is defined during the processing time within the SOAP request.

These two options are described in the following sections.

Request with a Template

An XML-coded job description can be stored on the server so that the client can refer to it in its SOAP request and thus the job does not have to be configured during the processing time.

The structure of the message will be explicated with the following example:

Example 7.1. Example of a SOAP Request with 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>
          


Beside the pre-defined elements for header and body in SOAP messages, the specific element run addresses the method run which is offered by the webservice.

This method defines a job (see Chapter 4, System Architecture) which is pre-defined by a template (see the section called “Definition of Job Templates”). The location of the corresponding template on the server is given by the attribute templateLocation. If variables are defined in the template, they can be configured (i. e. their default values can be overwritten) with the help of property elements. The attribute messageID is optional. The client can freely assign it and it will then be adopted in the server's reply.

Data streams which are to be processed are referenced with the help of stream elements in the SOAP message. Information about the individual ID (uuid) and the MIME type are optional. If the MIME type is unknown but specified in the message, unknown/* should be used.

If multiple StreamInputNodes are defined in the template file, you need to clearly allocate which data stream should be sent to which StreamInputNode. This allocation is realized through the nodeId attribute which refers to the ID assigned to the StreamInputNode within the template (attribute id).

The actual data can be assigned as BASE64-coded data stream with the tag documentData or referenced in a multipart/related container which features the CID (contentID) specified here if the MTOM feature is activated.

Job Definition Within the SOAP message

If the client is not supposed to use a job configuration pre-defined on the server, it is possible to incorporate this configuration in the SOAP request. The format used is similar to the one in a separate job template (see the section called “Definition of Job Templates”). Instead of the root element job, the definition is embedded in the SOAP request as a configuration element.

The following example illustrates this:

Example 7.2. Example of a SOAP Request with Embedded 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 this example two StreamInputNodes are connected through a DemultiplexerNode and the input data is returned to the client without changes.

The definition of the Nodes and their designated workflow graph is described in the configuration block.

Furthermore, this example shows how to connect specific input streams to a StreamInputNode: The first document is connected to the first StreamInputNode (nodeId input1) and the second document to the second StreamInputNode (nodeId input2).

Structure of a SOAP Response

The structure of a reply which is sent to the client in return for his request is also specified in the above-mentioned WSDL.

A reply could look like the following example:

Example 7.3. Example of a SOAP Reply

  <<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>
          


Beside a (potentially empty) number of resulting streams, which are respectively referenced to a multipart/related container with the help of an unambiguous ID, this reply also contains a status message. The following values are possible:

Value Meaning

COMPLETED

Job has been completed.

FAILED

Job could not be completed.

In both cases the return element can enclose a number of log-entry elements which contain hints on the failure of the processing or messages which occurred during the processing (see the section called “Creating a JobListener”). The following example shows the error message occurring if a job template file which does not exist is referenced:

Example 7.4. Example of a SOAP Reply with Error Message

  <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 of Job Templates

The definition of job templates spares the clients from knowing the internal steps of jadice server which are necessary to convert documents. These steps are provided for the webservice interface at a central location. Thus, the client only needs to know the webservice method run and the location of the desired template (these are usually filed in the subdirectory /server-config/).

The directory /server-config/jobtemplates contains the XSD definition of these templates.

An example for such a template is the template x2pdf.xml, which is included in the distribution package. Similar to the example in the section called “Converting Unknown Entry Data into a Consistent Format (PDF)”, this template identifies unknown input data and converts them to PDF format:

Example 7.5. Example for a Job Template

  <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>
          


As becomes obvious, a template is divided into three blocks:

  • Properties which apply to the whole job (timeouts etc.)

  • Definition of individual Nodes (element node) and their properties

  • Connecting the Nodes to a workflow which needs to form an acyclical graph.

As long as the individual Nodes and their properties adhere to the conventions for java beans, the respective properties can just be put above their names. As the above example shows, it is also possible to make them flexible, according to the following pattern: ${<key>} or ${<key>:<default value>}, in which the key has to start with a letter and may have letters, numbers, _ (underscore), - (hyphen) and . (period).

Because of this key, these values can be set (i. e. overwritten) as property (element <property name="key">value</property>) in the SOAP request. If variables that have no default value are not defined, the following error occurs on request: com.thoughtworks.xstream.converters.ConversionException: Pattern refers to undefined variable <key> for which there is no default

The individual Node elements need to feature an ID that is unambiguous to the template. With the help of these IDs, they are connected to a workflow in the connections block.

If data are to be transferred from the client to the server via the SOAP request that belongs to this template, you need to define at least one StreamInputNode. If there are multiple StreamInputNodes, the individual stream elements have to be allocated to their Nodes with the help of the attribute nodeId in the SOAP request. This step is superfluous if there is only one StreamInputNode.

The data streams generated in the StreamOutputNodes are sent back to the client as MTOM attachments in the SOAP reply. It is possible to define multiple StreamOutputNodes. The order in which the StreamOutputNodes are then called to attach their data streams onto the SOAP reply is random.

In order to embed job templates in a SOAP request (see section the section called “Job Definition Within the SOAP message”), the root element job needs to be removed; its content is attached to the element configuration in the SOAP request. In order to declare the job type in the Java code (see Example 6.2, “Configuring and Executing a Job”), the XML attribute type is used.

Generating Webservice Clients

Since the webservice interface is clearly defined through the WSDL, freely available webservice libraries can process this definition and generate proxy classes from it. These proxy classes can package the necessary SOAP requests and therefore enable an efficient development of client applications. This section shows this by looking at Sun's reference implementation of JAX-WS, the library Apache Axis2 and the library Apache CXF.

JAX-WS Reference Implementation

The distribution package of Java Development Kit (JDK) Version 1.7 contains the command line tool wsimport with which proxy classes can be generated. If the webservice of jadice server has been activated as described in the section called “Configuring the SOAP Interface”, the necessary client classes are generated with the following command:

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

The parameter -keep effectuates that not only classes but also their source codes are saved. We recommend to work with these in further development. Figure 7.2, “Classes generated by JAX-WS” shows the generated classes which can be incorporated into the developing environment.

Figure 7.2. Classes generated by JAX-WS

JAX-WS generates several classes from the WSDL, whcih can be directly incorporated into the developing environment.

The classes JadiceServerJobInvoker and JobInvocationService are entry points for a client application since they enable access to the SOAP interface. Another essential class is JobConfiguration with which the request is configured. A minimal implementation can look as follows:

Example 7.6. Implementing a SOAP Client by way of a JAX-WS Reference Implementation

// 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();
  …      
}


If the security interface of jadice server has been activated as described in the section called “Configuring the Security Interface”, a security header has to be added to the SOAP request on the client's side. The following example shows this with the help of the JAVA-WS reference implementation. A SOAPHandler generates the necessary security header:

Example 7.7. Implementing a 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;
  }


This handler is assigned to the invoker with the help of a HandlerResolver. In order to assign the handler, the following segment of the client code needs to be adjusted:

Example 7.8. Implementing a Security Header by way of a JAX-WS Reference Implementation

// 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 is available under Apache License under http://axis.apache.org/axis2/java/core/. If the webservice of jadice server has been activated as described in section the section called “Configuring the SOAP Interface”, the necessary client classes are generated with the following command:

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

The use of the parameters -o for the output directory and -p for the designated package name are optional. The parameter -d determines which data binding shall be used for the conversion from / to XML. The Apache Axis Data Binding (ADB) is the default binding. However, in its current version it struggles with the deserialization of SOAP/MTOM attachments, which is why we recommend using the JAX-B reference implementation (jaxbri) instead.

The classes JadiceServerJobInvokerStub and Run are entry points for a client application since they enable access to the SOAP interface. Another essential class is JobConfiguration with which the request is configured. A minimal implementation can look as follows:

Example 7.9. Implementing a SOAP Client by way of 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();
  …      
}


If the security interface of jadice server has been activated as described in the section called “Configuring the Security Interface”, a security header has to be added to the SOAP request on the client side. The following example shows this implementation by way of Apache Axis2.

Example 7.10. Generating a 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;
}


The generated header is assigned to be used for each webservice request with the help of the following adjustments to the webservice code on the client side:

Example 7.11. Implementing a Security Headers by way of Apache Axis2

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


Apache CXF

Apache CXF is available at http://cxf.apache.org/ and is under Apache License. If the webservice of jadice server has been activated as described in the section called “Configuring the SOAP Interface”, the following command generates the necessary client classes:

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

Using the parameters -d for the output directory and -p for the package name to be used are optional.

The classes JadiceServerJobInvoker and Run are entry points for a client application since they enable the access to the SOAP interface. The class JobConfiguration is also essential as the request is configured with its help. A minimal implementation solution can look as follows:

Example 7.12. Implementing a SOAP-Client by way of 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();
  …      
}


If the security interface of jadice server has been activated as described in the section called “Configuring the Security Interface”, a security header has to be added to the SOAP request on the client side. In order to do so, you can use the same syntax as in the JAX-WS reference implementation, see the section called “JAX-WS Reference Implementation”

[jadice server Version 5.8.7.0: Documentation for Developers and Administrators. Published: 2021-04-15]