Connecting to Campaign Monitor using Java, Spring, JAX-WS and Maven
This is a recipe for calling the Campaign Monitor Web Service API from a Java/Spring application that is built with Maven.
Generating client classes
First step is to generate client classes from the WSDL file. This task can be left to the jaxws-maven-plugin. It suffices to copy the wsdl file to a dedicated web service definition folder and configure the plug-in to use this folder:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
</execution>
</executions>
<configuration>
<wsdlDirectory>${basedir}/src/main/java/de/ralfebert/webservices</wsdlDirectory>
<bindingDirectory>${basedir}/src/main/java/de/ralfebert/webservices</bindingDirectory>
</configuration>
</plugin>
</plugins>
</build>
This generates the client classes automatically under target/jaxws/wsimport/java when the project is built. You can trigger the generation manually using:
mvn generate-sources
Customizing the client classes
Unfortunately, the generation yields a naming conflict for one of the wsdl types:
[ERROR] A class/interface with the same name "com.createsend.api.api.SubscriberUnsubscribe"
is already in use. Use a class customization to resolve this conflict.
line 92 of src/main/java/de/ralfebert/webservices/wsdl/campaignmonitor.wsdl
[ERROR] (Relevant to above error) another "SubscriberUnsubscribe" is generated from here.
line 139 of src/main/java/de/ralfebert/webservices/wsdl/campaignmonitor.wsdl
[ERROR] Two declarations cause a collision in the ObjectFactory class.
line 139 of src/main/java/de/ralfebert/webservices/wsdl/campaignmonitor.wsdl
Also, I wasn’t happy with the package and class name which were automatically generated from the schema URL http://api.createsend.com/api/ and the service name api.
These things can be customized using a binding file wsdl-customization.xml:
<?xml version="1.0" encoding="utf-8"?>
<jaxws:bindings
wsdlLocation="campaignmonitor.wsdl"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xsi:schemaLocation="
http://java.sun.com/xml/ns/jaxws http://java.sun.com/xml/ns/jaxws/wsdl_customizationschema_2_0.xsd
http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
<jaxws:package name="com.campaignmonitor.services" />
<jaxws:bindings node="//wsdl:portType[@name='apiSoap']">
<jaxws:class name="CampaignMonitorService" />
</jaxws:bindings>
<jaxws:bindings node="//xs:schema[@targetNamespace='http://api.createsend.com/api/']">
<jaxws:bindings node="xs:complexType[@name='SubscriberUnsubscribe']">
<jaxb:class name="SubscriberUnsubscribeType"/>
</jaxws:bindings>
<jaxb:schemaBindings>
<jaxb:package name="com.campaignmonitor.types"/>
</jaxb:schemaBindings>
</jaxws:bindings>
</jaxws:bindings>
Configuring the service as Spring Bean
The Spring framework provides a factory class JaxWsPortProxyFactoryBean to configure an instance of the generated interface CampaignMonitorService:
<bean id="campaignMonitorService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="com.campaignmonitor.services.CampaignMonitorService" />
<property name="wsdlDocumentUrl" value="classpath:de/ralfebert/webservices/campaignmonitor.wsdl" />
<property name="namespaceUri" value="http://api.createsend.com/api/" />
<property name="serviceName" value="api" />
<property name="endpointAddress" value="https://api.createsend.com/api/api.asmx" />
</bean>
Thanks to Gunnar Morling for giving an example in his blog: Writing and testing JAX-WS clients using the Spring framework
Writing a newsletter service
Unfortunately the Campaign Monitor API is rather rudimentary - most types are simple Strings or Objects, return codes are used instead of SOAP faults. So I wrapped the needed web service calls in a convenient NewsletterService which handles subscribing and unsubscribing to one subscriber list and can easily be extended with other operations:
@Service
public class NewsletterService {
private static final Logger log = LoggerFactory.getLogger(NewsletterService.class);
@Autowired
private CampaignMonitorService campaignMonitorService;
private String apiKey;
private String listId;
public void subscribeNewsletter(String email) {
SubscriberAddAndResubscribe request = new SubscriberAddAndResubscribe();
request.setApiKey(apiKey);
request.setListID(listId);
request.setName("");
request.setEmail(email);
Result r = campaignMonitorService.addAndResubscribe(request).getSubscriberAddAndResubscribeResult();
if (r.getCode() != 0) {
throw new RuntimeException(r.getCode() + " " + r.getMessage());
}
}
public void unsubscribeNewsletter(String email) {
SubscriberUnsubscribe unsubscribe = new SubscriberUnsubscribe();
unsubscribe.setApiKey(apiKey);
unsubscribe.setListID(listId);
unsubscribe.setEmail(email);
Result r = campaignMonitorService.unsubscribe(unsubscribe).getSubscriberUnsubscribeResult();
if (r.getCode() == 202) {
// IGNORE: Subscriber not in list or has already been removed
} else if (r.getCode() != 0) {
throw new RuntimeException(r.getCode() + " " + r.getMessage());
}
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
public void setListId(String listId) {
this.listId = listId;
}
}
This is configured as a Spring bean and is used by other services that need to talk to the Campaign Monitor services:
<bean id="newsletterService" class="de.ralfebert.core.company.service.NewsletterService">
<property name="apiKey" value="${campaignMonitor.apiKey}"/>
<property name="listId" value="${campaignMonitor.listId}"/>
</bean>
This recipe is licensed under CC-BY-SA, the code examples are licensed under the BSD license.

