How to provision Open-Xchange with Java and SOAP

Overview

We use Apache CXF as framework to implement SOAP with Java.

Apache CXF comes with a tool called wsdl2java which reads the WSDL published by an OX App Suite installation and generates classes which represent corresponding SOAP objects and method calls.

So the general procedure is:

  • Setup OX including the SOAP admin interface
  • Create classes with wsdl2java
  • Use these classes in your Java code to make SOAP calls

Generating code with wsdl2java from Apache CXF

See the sample script below on how to generate stubs for the Open-Xchange provisioning APIs.

#!/bin/bash

# 1. download apache-cxf tarball, extract it and "cd" into the directory
#    (this script has been developed and verified with CXF versions 3.0 and 3.1)
# 2. change variables CODEBASE, JAVA_HOME and WSDLURL
# 3. run this script "bash oxaas-wsdl2java"
export JAVA_HOME="/usr/lib/jvm/java-1.7.0-openjdk-amd64"
CODEBASE="./codebase"
WSDLURL="http://192.168.101.84/webservices"
CXF_HOME=/opt/apache-cxf-3.0.5
 
rm -rf $CODEBASE
frontend=jaxws21
dbinding=jaxb
 
JAXBTMP=/tmp/jaxb$$.xml
rm -f $JAXBTMP
cat<<EOF > $JAXBTMP
<jaxb:bindings version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <jaxb:globalBindings generateElementProperty="false"/>
</jaxb:bindings>
EOF
 
for class in Context User Group Resource; do
    lname=$(echo $class | tr '[:upper:]' '[:lower:]')
    pname="com.openexchange.$lname"

    $CXF_HOME/bin/wsdl2java -encoding utf8 -databinding $dbinding -frontend $frontend -client -impl -d $CODEBASE -keep -b $JAXBTMP \
        -p "http://soap.admin.openexchange.com=admin.soap.${lname}" \
        -p "http://dataobjects.soap.admin.openexchange.com/xsd=${pname}.soap.dataobjects" \
        -p "http://dataobjects.rmi.admin.openexchange.com/xsd=${pname}.rmi.dataobjects" \
        -p "http://exceptions.rmi.admin.openexchange.com/xsd=${pname}.rmi.exceptions" \
        -p "http://rmi.java/xsd=${pname}.java.rmi" \
        -p "http://io.java/xsd=${pname}.java.io" \
        ${WSDLURL}/OX${class}Service?wsdl
done
 
rm -f $JAXBTMP

The script generates stubs for each of the Context, User, Group and Resource API within a separate subdirectory of the $CODEBASE directory.

Example Code

Building the example code given below either requires some build tool / IDE of your choice (whose configuration is out of scope of this article) or you can build and run the example code manually like

# clean up old stuff from previous run
rm -rf codebase *.class

# generate java classes and compile them
./ox-wsdl2java
find codebase -name \*.java | xargs javac

# compile and run sample program
javac -cp codebase MyContextClientExample.java MyUserClientExample.java
java -cp .:codebase MyContextClientExample
java -cp .:codebase MyUserClientExample

Sample client code follows below.

MyContextClientExample.java shows createcontext, listcontext, deletecontext:

import admin.soap.context.*;
import com.openexchange.context.rmi.dataobjects.Credentials;
import com.openexchange.context.soap.dataobjects.Context;
import com.openexchange.context.soap.dataobjects.User;
import com.openexchange.context.soap.dataobjects.SchemaSelectStrategy;

import javax.xml.namespace.QName;
import java.io.IOException;
import java.util.List;

/*
 * Example SOAP client for OXContextService
 *
 * creates a context, calls listcontext, deletes the context again
 */
public class MyContextClientExample {
 
    private static final QName SERVICE_NAME = new QName("http://soap.admin.openexchange.com", "OXContextService");
 
    public static void main(String[] args) {
        final String subadminname = "oxadminmaster";
        final String subadminpw   = "secret";
        final String ctxname      = "soapcontext";
 
        Credentials creds = new Credentials();
        Context ctx = new Context();
        User oxadmin = new User();
 
        OXContextService contextservice = new OXContextService(OXContextService.WSDL_LOCATION, SERVICE_NAME);
        OXContextServicePortType contextport = contextservice.getOXContextServiceHttpsEndpoint();

        creds.setLogin(subadminname);
        creds.setPassword(subadminpw);
 
        ctx.setName(ctxname);
        ctx.setMaxQuota(10000l);
        ctx.setId(10001);
 
        final String adminEmail = "oxadmin@example.com";
        final String ctxadmname = "oxadmin";
        final String ctxadmpw   = "secret";
        oxadmin.setName(ctxadmname);
        oxadmin.setPassword(ctxadmpw);
        oxadmin.setDisplayName("OX Admin");
        oxadmin.setSurName("OX");
        oxadmin.setGivenName("Admin");
        oxadmin.setPrimaryEmail(adminEmail);
        oxadmin.setEmail1(adminEmail);
        oxadmin.setDefaultSenderAddress(adminEmail);
        oxadmin.setLanguage("en_US");
        oxadmin.setTimezone("Europe/Berlin");

        CreateModuleAccessByName cmab = new CreateModuleAccessByName();
        cmab.setAccessCombinationName("groupware_premium");
        cmab.setAdminUser(oxadmin);
        cmab.setAuth(creds);
        cmab.setCtx(ctx);

        SchemaSelectStrategy strategy = new SchemaSelectStrategy();
        strategy.setStrategy("automatic");

        try {
            Context ret = contextport.createModuleAccessByName(ctx, oxadmin, "groupware_premium", creds, strategy);
            System.out.println("created context with id=" + ret.getId());

            System.out.println("existing contexts:");
            List<Context> allctxs = contextport.listAll(creds);
            for (final Context c : allctxs) {
                System.out.println(c.getName() + " with id=" + c.getId());
            }

            System.out.println("hit enter to continue");
            System.in.read();

            System.out.println("deleting created context again");
            Delete del = new Delete();
            del.setAuth(creds);
            del.setCtx(ctx);
            contextport.delete(del);

        } catch (RemoteExceptionException e) {
            e.printStackTrace();
        } catch (StorageExceptionException e) {
            e.printStackTrace();
        } catch (InvalidCredentialsExceptionException e) {
            e.printStackTrace();
        } catch (InvalidDataExceptionException e) {
            e.printStackTrace();
        } catch (ContextExistsExceptionException e) {
            e.printStackTrace();
        } catch (NoSuchContextExceptionException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DatabaseUpdateExceptionException e) {
            e.printStackTrace();
        }
    }
}

MyUserClientExample.java shows createuser, deleteuser:

import admin.soap.user.*;
import com.openexchange.user.rmi.dataobjects.Credentials;
import com.openexchange.user.soap.dataobjects.Context;
import com.openexchange.user.soap.dataobjects.User;

import javax.xml.namespace.QName;
import java.io.IOException;

/*
 * Example SOAP client for OXUserService
 *
 * Create a user and delete it again
 *
 */
public class MyUserClientExample {

    private static final QName SERVICE_NAME = new QName("http://soap.admin.openexchange.com", "OXUserService");

    public static void main(String[] args) {
        final String adminname = "oxadmin";
        final String adminpw   = "secret";
        final int ctxid        = 10001;

        Credentials creds = new Credentials();
        Context ctx = new Context();
        User oxuser = new User();

        OXUserService userservice = new OXUserService(OXUserService.WSDL_LOCATION, SERVICE_NAME);
        OXUserServicePortType userport = userservice.getOXUserServiceHttpsEndpoint();

        creds.setLogin(adminname);
        creds.setPassword(adminpw);

        ctx.setId(ctxid);

        oxuser.setName("oxuser1");
        oxuser.setPassword("secret");
        oxuser.setDisplayName("OX User 1");
        oxuser.setSurName("OX");
        oxuser.setGivenName("User 1");
        oxuser.setPrimaryEmail("oxuser1@soapcontext");
        oxuser.setEmail1("oxuser1@soapcontext");
        oxuser.setDefaultSenderAddress("oxuser1@soapcontext");
        oxuser.setImapLogin("oxuser1@soapcontext");
        oxuser.setImapServer("localhost");
        oxuser.setSmtpServer("localhost");
        oxuser.setLanguage("en_US");
        oxuser.setTimezone("Europe/Berlin");

        try {
            User ret = userport.createByModuleAccessName(ctx, oxuser, "groupware_premium", creds);
            System.out.println("created user with id=" + ret.getId());

            System.out.println("hit enter to continue");
            System.in.read();

            System.out.println("deleting created user again");
            Delete del = new Delete();
            del.setAuth(creds);
            del.setCtx(ctx);
            del.setUser(oxuser);
            userport.delete(del);
        } catch (RemoteExceptionException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DatabaseUpdateExceptionException e) {
            e.printStackTrace();
        } catch (StorageExceptionException e) {
            e.printStackTrace();
        } catch (InvalidCredentialsExceptionException e) {
            e.printStackTrace();
        } catch (InvalidDataExceptionException e) {
            e.printStackTrace();
        } catch (NoSuchContextExceptionException e) {
            e.printStackTrace();
        } catch (NoSuchUserExceptionException e) {
            e.printStackTrace();
        }
    }
}

Finally sometimes it is required to not directly make a SOAP call, but to generate the request body separately. One example therefore are gatling performance tests, where we can make SOAP tests if we just generate the SOAP (=HTTP POST) bodies separately and make, from the gatling point of view, simple HTTP POST requests.

Full working code therefore can be found in our gatling git repo, see gatling for details. The relevant steps to create the SOAP body itself are given below. It is scala code; how to translate it to Java (if required) should be straightforward.

import admin.soap.context.{ CreateModuleAccessByName, CreateModuleAccessByNameResponse }
import com.openexchange.context.rmi.dataobjects.Credentials
import com.openexchange.context.soap.dataobjects.{Context, Database, User}

import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
import javax.xml.bind.{JAXBContext, Marshaller, Unmarshaller}
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.soap.{MessageFactory, SOAPMessage}
import org.w3c.dom.Document

val creds: Credentials = new Credentials
val ctx: Context = new Context
val oxadmin: User = new User
// fill relevant fields of the creds, ctx, oxadmin oxbjects

val cmab: CreateModuleAccessByName = new CreateModuleAccessByName
cmab.setAccessCombinationName("all")
cmab.setAdminUser(oxadmin)
cmab.setAuth(creds)
cmab.setCtx(ctx)

val jaxbContext: JAXBContext = JAXBContext.newInstance(classOf[CreateModuleAccessByName])
val jaxbMarshaller: Marshaller = jaxbContext.createMarshaller
jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, true)

val document: Document = DocumentBuilderFactory.newInstance.newDocumentBuilder.newDocument
jaxbMarshaller.marshal(cmab, document)

val soapMessage: SOAPMessage = MessageFactory.newInstance.createMessage

// optional cosmetic: change namespace from SOAP-ENV to soap to match other conventions
soapMessage.getSOAPPart().getEnvelope().removeNamespaceDeclaration("SOAP-ENV")
soapMessage.getSOAPPart().getEnvelope().addNamespaceDeclaration("soap", "http://www.w3.org/2001/12/soap-envelope")
soapMessage.getSOAPPart().getEnvelope().setPrefix("soap")
soapMessage.getSOAPHeader().setPrefix("soap")
soapMessage.getSOAPBody().setPrefix("soap")
// optional cosmetic end

soapMessage.getSOAPBody.addDocument(document)

val outputStream: ByteArrayOutputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
val soapBody: String = outputStream.toString()