How to consume SOAP web services in AEM 6.5

AEM Tips

SOAP is a web protocol that is more than 20 years old, yet it is still commonly used in the enterprise environment to build and consume web services. 

Integrating SOAP service can be a complex topic, especially in combination with Adobe Experience Manager (AEM) and OSGi.

The following tutorial demonstrates how to build a simple SOAP client and server. 

Finally, we will also show how to integrate SOAP web services into AEM 6.5.

We recommend starting with a simple and isolated test setup before starting with your actual project. 

This is precisely what we are going to build today.

The setup will consist of the following:

  1. Spring Boot based SOAP Server
  2. CLI SOAP client to consume the service
  3. Integration of the SOAP client into Adobe Experience Manager (AEM)

At the end of the tutorial, you will find the complete code of all three steps. 

Please note that we will focus on Java 8 and Java 11 since Adobe supports only the LTS versions for AEM. There will be some minor changes for Java 11.

To complete this tutorial, we assume you have a basic understanding of Java and Maven. Also, for chapter 3, AEM knowledge is required.

Let’s get started.

Test Spring Boot SOAP server

In the following, we will show a step-by-step guide to build a SOAP server for a given XML Schema Definition (XSD) file. 

It is called the “Contract first approach“. It does not matter if you use Java 8 or Java 11 for this chapter.

The XSD we use describes a service that allows conversion between Celsius and Fahrenheit.

Setup the Spring project

Create the project directory spring-soap-server and an empty pom.xml.

      <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.one_inside.blog.soap</groupId>
    <artifactId>spring-soap-server</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <name>spring-soap-server</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- For Java 8 use '1.8' -->
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
</project>
    

Add the Spring Boot parent and the web services and WSDL dependencies to pom.xml

       <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
        </dependency>
        <!-- Only required for Java 11 -->
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-ri</artifactId>
            <version>2.3.3</version>
            <type>pom</type>
        </dependency>
    </dependencies>
    

Add the Spring Boot entry point by creating the following class Application in the directory src/main/java/com/one_inside/blog/soapserver

      package com.one_inside.blog.soapserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
    

Now you can start the spring boot server by executing mvn spring-boot:run.

By default, the server binds to port 8080. You should see an error message when you open http://localhost:8080/

Generate the Domain Classes from XSD

The “Contract first approach” requires an existing domain definition XSD file describing the service’s methods and parameters.

The WSDL file will be automatically generated later.

Add the jaxb2-maven-plugin to pom.xml. This Plugin will be responsible to create the Java classes for the given XSD.

      <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>2.5.0</version>
                <executions>
                    <execution>
                        <id>xjc</id>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <sources>
                        <source>${project.basedir}/src/main/resources/temperature-conversion.xsd</source>
                    </sources>
                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

Create the XSD file src/main/resources/temperature-conversion.xsd.

      <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.one-inside.com/blog/soapserver/gen"
           targetNamespace="http://www.one-inside.com/blog/soapserver/gen" elementFormDefault="qualified">

    <xs:element name="convertCelsiusToFahrenheitRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="celsius" type="xs:float"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="convertCelsiusToFahrenheitResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="fahrenheit" type="xs:float"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="convertFahrenheitToCelsiusRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="fahrenheit" type="xs:float"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="convertFahrenheitToCelsiusResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="celsius" type="xs:float"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
    

Run mvn clean compile.

If all goes well xjc has automatically generated the classes and copied them to the directory src/main/java/com/one_inside/blog/soapserver/gen.

Implement the SOAP web service endpoint

Now we have to implement the service and providing the implementation of the endpoint.


Create the following three classes in src/main/java/com/one_inside/blog/soapserver

      package com.one_inside.blog.soapserver;

import org.springframework.stereotype.Component;

@Component
public class TemperatureConversionService {

    public float celsiusToFahrenheit(float celsius) {
        return celsius * 1.8f + 32.00f;
    }

    public float fahrenheitToCelsius(float fahrenheit) {
        return (fahrenheit - 32.0f) / 1.8f;
    }

}

    

The service to calculate the temperature conversion:

      package com.one_inside.blog.soapserver;

import com.one_inside.blog.soapserver.gen.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

import static com.one_inside.blog.soapserver.WebServiceConfig.TARGET_NAMESPACE;

@Endpoint
public class TemperatureConversionEndpoint {

    private final TemperatureConversionService temperatureConversionService;

    private final ObjectFactory objectFactory = new ObjectFactory();

    @Autowired
    public TemperatureConversionEndpoint(TemperatureConversionService temperatureConversionService) {
        this.temperatureConversionService = temperatureConversionService;
    }

    @PayloadRoot(namespace = TARGET_NAMESPACE, localPart = "convertCelsiusToFahrenheitRequest")
    @ResponsePayload
    public ConvertCelsiusToFahrenheitResponse convertCelsiusToFahrenheit(@RequestPayload ConvertCelsiusToFahrenheitRequest request) {
        ConvertCelsiusToFahrenheitResponse response = objectFactory.createConvertCelsiusToFahrenheitResponse();
        response.setFahrenheit(temperatureConversionService.celsiusToFahrenheit(request.getCelsius()));
        return response;
    }

    @PayloadRoot(namespace = TARGET_NAMESPACE, localPart = "convertFahrenheitToCelsiusRequest")
    @ResponsePayload
    public ConvertFahrenheitToCelsiusResponse convertFahrenheitToCelsius(@RequestPayload ConvertFahrenheitToCelsiusRequest request) {
        ConvertFahrenheitToCelsiusResponse response = objectFactory.createConvertFahrenheitToCelsiusResponse();
        response.setCelsius(temperatureConversionService.fahrenheitToCelsius(request.getFahrenheit()));
        return response;
    }
}
    

The webservice endpoint:

      package com.one_inside.blog.soapserver;

import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

    public static final String TARGET_NAMESPACE = "http://www.one-inside.com/blog/soapserver/gen";

    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");
    }

    @Bean(name = "temperature-conversion")
    public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema temperatureConversionSchema) {
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setSchema(temperatureConversionSchema);
        wsdl11Definition.setPortTypeName("TemperatureConversionPort");
        wsdl11Definition.setTargetNamespace(TARGET_NAMESPACE);
        wsdl11Definition.setLocationUri("/ws");
        return wsdl11Definition;
    }

    @Bean
    public XsdSchema temperatureConversionSchema() {
        return new SimpleXsdSchema(new ClassPathResource("temperature-conversion.xsd"));
    }
}
    

The binding code of the web service and annotations to later generate the WSDL file.

Now (re)run the command mvn spring-boot:run and open http://localhost:8080/ws/temperature-conversion.wsdl.

You should see the generated WSDL file with the Service Port TemperatureConversionPort which we will need in the following chapter.

That’s it for the server part. We now have a working Spring Server providing a SOAP Webservice Endpoint. In the next chapter, we are looking at how to invoke this service from a command-line client.

Consuming a SOAP web service with a Java CLI Application

In this chapter, we are building a simple CLI application with the generated code from the WSDL file to invoke the web service. If you did not follow the first chapter, you could use the following file to generate the classes, but of course, you would not be able to call the service.

      <?xml version="1.0" encoding="UTF-8" standalone="no"?><wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://www.one-inside.com/blog/soapserver/gen" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.one-inside.com/blog/soapserver/gen" targetNamespace="http://www.one-inside.com/blog/soapserver/gen">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.one-inside.com/blog/soapserver/gen">

            <xs:element name="convertCelsiusToFahrenheitRequest">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="celsius" type="xs:float"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="convertCelsiusToFahrenheitResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="fahrenheit" type="xs:float"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>

            <xs:element name="convertFahrenheitToCelsiusRequest">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="fahrenheit" type="xs:float"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="convertFahrenheitToCelsiusResponse">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="celsius" type="xs:float"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="convertCelsiusToFahrenheitResponse">
        <wsdl:part element="tns:convertCelsiusToFahrenheitResponse" name="convertCelsiusToFahrenheitResponse">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="convertFahrenheitToCelsiusResponse">
        <wsdl:part element="tns:convertFahrenheitToCelsiusResponse" name="convertFahrenheitToCelsiusResponse">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="convertCelsiusToFahrenheitRequest">
        <wsdl:part element="tns:convertCelsiusToFahrenheitRequest" name="convertCelsiusToFahrenheitRequest">
        </wsdl:part>
    </wsdl:message>
    <wsdl:message name="convertFahrenheitToCelsiusRequest">
        <wsdl:part element="tns:convertFahrenheitToCelsiusRequest" name="convertFahrenheitToCelsiusRequest">
        </wsdl:part>
    </wsdl:message>
    <wsdl:portType name="TemperatureConversionPort">
        <wsdl:operation name="convertCelsiusToFahrenheit">
            <wsdl:input message="tns:convertCelsiusToFahrenheitRequest" name="convertCelsiusToFahrenheitRequest">
            </wsdl:input>
            <wsdl:output message="tns:convertCelsiusToFahrenheitResponse" name="convertCelsiusToFahrenheitResponse">
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="convertFahrenheitToCelsius">
            <wsdl:input message="tns:convertFahrenheitToCelsiusRequest" name="convertFahrenheitToCelsiusRequest">
            </wsdl:input>
            <wsdl:output message="tns:convertFahrenheitToCelsiusResponse" name="convertFahrenheitToCelsiusResponse">
            </wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="TemperatureConversionPortSoap11" type="tns:TemperatureConversionPort">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="convertCelsiusToFahrenheit">
            <soap:operation soapAction=""/>
            <wsdl:input name="convertCelsiusToFahrenheitRequest">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="convertCelsiusToFahrenheitResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="convertFahrenheitToCelsius">
            <soap:operation soapAction=""/>
            <wsdl:input name="convertFahrenheitToCelsiusRequest">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="convertFahrenheitToCelsiusResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="TemperatureConversionPortService">
        <wsdl:port binding="tns:TemperatureConversionPortSoap11" name="TemperatureConversionPortSoap11">
            <soap:address location="http://localhost:8080/ws"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>
    

With Java 8, there was a command-line utility called wsimport, which shipped with the JDK. For Java 11, it was removed and instead has to be called from a Java application.

If you are still using Java 8 you can try to execute the following:
wsimport http://localhost:8080/ws/temperature-conversion.wsdl. This should create a folder com containing the generated SOAP client.

You can delete the folder again.

Since first of all, this tool doesn’t exist anymore with Java 11, and secondly, in many production setups, the WSDL should be generated during compile time. We will show how to generate the classes with Maven.

Create a new directory called cli-soap-client on the same level you created spring-soap-server and the following pom.xml file.

      <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.one_inside.blog</groupId>
    <artifactId>cli-soap-client</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <name>cli-soap-client</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- For Java 8 use '1.8' -->
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <exec.mainClass>com.one_inside.blog.soapclient.ClientTest</exec.mainClass>
    </properties>
    
    <dependencies>
        <!-- Only required for Java 11 -->
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-ri</artifactId>
            <version>2.3.3</version>
            <type>pom</type>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>wsimport-from-jdk</id>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <wsdlUrls>
                        <wsdlUrl>http://localhost:8080/ws/temperature-conversion.wsdl</wsdlUrl>
                         <!-- Link to local file if you don't want to generate the code with the live wsdl file
                         <wsdlUrl>${project.basedir}/src/main/resources/temperature-conversion.wsdl</wsdlUrl>-->

                    </wsdlUrls>
                    <keep>true</keep>
                    <packageName>com.one_inside.blog.soapclient.gen</packageName>
                    <sourceDestDir>src/main/java</sourceDestDir>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
    

The jaxws-maven-plugin plugin is used to execute wsimport instead of the CLI utility. Note that you could also point to a local .wsdl file in place of the URL to the server we created in chapter 1.

Also, consider the added dependency jaxws-ri which will link all web service related code that was removed from the Java 11 JDK.

Also note the property exec.mainClass to link the main class we will create next.

Execute mvn clean compile to generate the classes for the SOAP client classes from the WSDL file.

Next, we can create the test client, create the following class src/main/java/com/one_inside/blog/soapclient/ClientTest.java:

      package com.one_inside.blog.soapclient;

import com.one_inside.blog.soapclient.gen.*;

public class ClientTest {
    public static void main(String[] args) {

        final float initialCelsius = 32.5f;
        TemperatureConversionPortService service = new TemperatureConversionPortService();
        TemperatureConversionPort temperatureConversionPort = service.getTemperatureConversionPortSoap11();

        ConvertCelsiusToFahrenheitRequest convertCelsiusToFahrenheitRequest = new ConvertCelsiusToFahrenheitRequest();
        convertCelsiusToFahrenheitRequest.setCelsius(initialCelsius);
        ConvertCelsiusToFahrenheitResponse convertCelsiusToFahrenheitResponse = temperatureConversionPort.convertCelsiusToFahrenheit(convertCelsiusToFahrenheitRequest);

        ConvertFahrenheitToCelsiusRequest convertFahrenheitToCelsiusRequest = new ConvertFahrenheitToCelsiusRequest();
        convertFahrenheitToCelsiusRequest.setFahrenheit(convertCelsiusToFahrenheitResponse.getFahrenheit());
        ConvertFahrenheitToCelsiusResponse convertFahrenheitToCelsiusResponse = temperatureConversionPort.convertFahrenheitToCelsius(convertFahrenheitToCelsiusRequest);

        String message = String.format("SUCCESS: %s °C =&gt; %s °F =&gt; %s °C",
                initialCelsius,
                convertCelsiusToFahrenheitResponse.getFahrenheit(),
                convertFahrenheitToCelsiusResponse.getCelsius());
        System.out.println(message);
    }
}
    

Because we have set the property exec.mainClass we can execute the test client with mvn exec:java

You should see the line SUCCESS: 32.5 °C => 90.5 °F => 32.5 °C

Integrating SOAP Client into AEM 6.5

Now all that is left is bringing the SOAP web service into AEM. With AEM 6.5 out of the box, SOAP web service cannot be consumed anymore. The required dependencies have been removed. We will have to bring them back into AEM.

First, let’s create a new project again. This time we can use the AEM Archetype. We will use the latest AEM version at the time (6.5.8), but you can also use an older Service Pack of 6.5.

      mvn -B archetype:generate \
 -D archetypeGroupId=com.adobe.aem \
 -D archetypeArtifactId=aem-project-archetype \
 -D archetypeVersion=27 \
 -D appTitle="AEM Soap Client Demo Project" \
 -D appId="aem-soap-client" \
 -D groupId="com.one_inside.blog" \
 -D aemVersion=6.5.8
    

Next, we have to repeat the step to generate the SOAP client code from the WSDL from the last chapter. For this, again copy the following to ./core/pom.xml

                  ...
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>wsimport-from-jdk</id>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <wsdlUrls>
                        <wsdlUrl>http://localhost:8080/ws/temperature-conversion.wsdl</wsdlUrl>
                        <!-- Link to local file if you don't want to generate the code with the live wsdl file
                        <wsdlUrl>${project.basedir}/src/main/resources/temperature-conversion.wsdl</wsdlUrl>-->
                    </wsdlUrls>
                    <keep>true</keep>
                    <packageName>com.one_inside.blog.soapclient.gen</packageName>
                    <sourceDestDir>src/main/java</sourceDestDir>
                </configuration>
            </plugin>
      </plugins>
    

Deploy everything as usual with mvn clean install -P autoInstallPackage. This should create the SOAP client classes in ./core/src/main/java/com/one_inside/blog/soapclient/gen

Next we will create code to execute the client. To trigger it easily we will put it into a test servlet in src/main/java/com/one_inside/blog/core/servlets.

      package com.one_inside.blog.core.servlets;

import com.one_inside.blog.soapclient.gen.*;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.annotations.SlingServletResourceTypes;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.propertytypes.ServiceDescription;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;

@Component(service = { Servlet.class })
@SlingServletResourceTypes(
        resourceTypes="cq/Page",
        methods=HttpConstants.METHOD_GET,
        selectors = "test-soap-client",
        extensions="html")
@ServiceDescription("Simple Demo Servlet")
public class SoapTestServlet extends SlingSafeMethodsServlet {

    private static final long serialVersionUID = 3665434509966506719L;

    @Override
    protected void doGet(final SlingHttpServletRequest req,
            final SlingHttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");

        final float initialCelsius = 32.5f;
        TemperatureConversionPortService service = new TemperatureConversionPortService();
        TemperatureConversionPort temperatureConversionPort = service.getTemperatureConversionPortSoap11();

        ConvertCelsiusToFahrenheitRequest convertCelsiusToFahrenheitRequest = new ConvertCelsiusToFahrenheitRequest();
        convertCelsiusToFahrenheitRequest.setCelsius(initialCelsius);
        ConvertCelsiusToFahrenheitResponse convertCelsiusToFahrenheitResponse = temperatureConversionPort.convertCelsiusToFahrenheit(convertCelsiusToFahrenheitRequest);

        ConvertFahrenheitToCelsiusRequest convertFahrenheitToCelsiusRequest = new ConvertFahrenheitToCelsiusRequest();
        convertFahrenheitToCelsiusRequest.setFahrenheit(convertCelsiusToFahrenheitResponse.getFahrenheit());
        ConvertFahrenheitToCelsiusResponse convertFahrenheitToCelsiusResponse = temperatureConversionPort.convertFahrenheitToCelsius(convertFahrenheitToCelsiusRequest);

        String message = String.format("SUCCESS: %s °C =&gt; %s °F =&gt; %s °C \uD83C\uDF89",
                initialCelsius,
                convertCelsiusToFahrenheitResponse.getFahrenheit(),
                convertFahrenheitToCelsiusResponse.getCelsius());

        resp.getWriter().write(message);
    }
}
    

The servlet can, of course, be invoked with the selector test-soap-client on any page, for example: http://localhost:4502/content/we-retail/language-masters/en.test-soap-client.html

This will not yet work since, as mentioned, the dependencies are missing. We have to add the Apache CXF dependencies too, for example, ./all/pom.xml as embedded dependencies to be deployed into the AEM runtime environment. The bundles’ list can be found here, but we had to do some minor modifications to make it work.

      <embeddeds>
    ...


    <!-- cxf -->
    <embedded>
        <groupId>org.apache.servicemix.bundles</groupId>
        <artifactId>org.apache.servicemix.bundles.wsdl4j</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>com.fasterxml.woodstox</groupId>
        <artifactId>woodstox-core</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-core</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-bindings-soap</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-bindings-xml</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-simple</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-databinding-jaxb</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-wsdl</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.apache.ws.xmlschema</groupId>
        <artifactId>xmlschema-core</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.codehaus.woodstox</groupId>
        <artifactId>stax2-api</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <embedded>
        <groupId>org.ow2.asm</groupId>
        <artifactId>asm-all</artifactId>
        <target>/apps/aem-soap-client-vendor-packages/application/install</target>
    </embedded>
    <!-- cxf end -->
<embeddeds>
   
...

<dependencies>
    ...
               
    <!-- cxf start -->
    <dependency>
        <groupId>org.apache.servicemix.bundles</groupId>
        <artifactId>org.apache.servicemix.bundles.wsdl4j</artifactId>
        <version>1.6.3_1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.woodstox</groupId>
        <artifactId>woodstox-core</artifactId>
        <version>5.0.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-core</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-bindings-soap</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-bindings-xml</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-simple</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-databinding-jaxb</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-wsdl</artifactId>
        <version>3.2.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.ws.xmlschema</groupId>
        <artifactId>xmlschema-core</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.woodstox</groupId>
        <artifactId>stax2-api</artifactId>
        <version>3.1.4</version>
    </dependency>
    <dependency>
        <groupId>org.ow2.asm</groupId>
        <artifactId>asm-all</artifactId>
        <version>5.0.4</version>
    </dependency>
    <!-- cxf end -->
</dependencies>
    

Deploy everything again by executing mvn clean install -PautoInstallPackage.

Now the servlet should work. You should see the following messaged displayed in the browser
SUCCESS: 32.5 °C => 90.5 °F => 32.5 °C

That’s it!

Congratulations!

Get the code

You can find the code of all three examples here.

We have built a simple playground:

We hope this will help your next project with AEM and production setup like proxy configs, authentication, testing, dynamic bindings and so on.

If you have any questions about the tutorial or want to learn more about integrating third-party services into AEM, feel free to reach out to us.

Basil Kohler

Basil Kohler

Senior AEM Architect

Would you like to receive the next article?

Subscribe to our newsletter and we will send you the next article about Adobe Experience Manager.