While working on a project recently I was faced with the task to define a couple of webservices. As my application is completely based on Spring, the logical choice (in my book) would be using Spring-WS. After some deliberation I decided to go with classic Soap based webservices, as the application that would be calling my services did not support REST. Also, Axis 1 was already in place, so integration with that framework was considered important.
I did however want the solution to be as hassle-free as possible. Even though it’s contract first, I didn’t want to spend a lot of time messing about with XSD’s and WSDL and decided to try and generate both of these artifacts based on my Java code. As JAXB2 supports annotation based definition of the resulting XML structure I decided to use that framework to fulfil my marshalling needs. Spring-WS integration with JAXB2 is pretty decent, so why not?
Maven2
I’m using maven to manage my dependencies, nothing special there. Additionally, I’m going to be looking to Maven to supply me with a mechanism for generating the XSD and packaging it within the application. First, I’ll start by adding the dependencies.
<dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-xml</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-oxm</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-oxm-tiger</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core-tiger</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-support</artifactId> <version>1.5.8</version> </dependency> <dependency> <groupId>sun-jaxb</groupId> <artifactId>jaxb-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>sun-jaxb</groupId> <artifactId>jaxb-impl</artifactId> <version>2.2</version> </dependency>
In this snippet I will assume for brevity that all required Spring 3 libraries have already been added. Drop me a line if you want to know the contents of the complete pom.
Servlet configuration
As Spring-WS works using a servlet to handle requests, the first thing I’ll have to do is set up this servlet. I’m going to handle requests using the /services/ mapping.
<servlet>
<servlet-name>services</servlet-name>
<servlet-class>
org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>services</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
This should allow Spring-WS to handly my requests. Interesting bit of configuration is the usage of the contextConfigLocation parameter. The value is omitted to allow the default contextLoaderListener to pick up the configuration and wire everything together. So, after handling this, next up is the Spring configuration.
Spring configuration
This is where it gets a little tricky. Well, actually it’s not that bad.
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping" /> <bean id="marshaller" class="org.walgemoed.util.ClasspathScanningJAXB2Marshaller"> <property name="schema" value="classpath:my-service.xsd" /> <property name="basePackage" value="org.walgemoed" /> </bean> <bean class="org.springframework.ws.server.endpoint.adapter.GenericMarshallingMethodEndpointAdapter"> <property name="marshaller" ref="marshaller" /> <property name="unmarshaller" ref="marshaller" /> </bean> <bean id="supportcms" class="org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition"> <property name="builder"> <bean class="org.springframework.ws.wsdl.wsdl11.builder.XsdBasedSoap11Wsdl4jDefinitionBuilder"> <property name="schema" value="classpath:my-service.xsd" /> <property name="portTypeName" value="my-service" /> <property name="locationUri" value="/services/" /> </bean> </property> </bean>
Four pieces of configuration are required to publish my services. The first bean I’m setting up is the PayloadRootAnnotationMethodEndpointMapping component. This Spring component will register any classes that are annotated with @Endpoint, and pick up any methods annotated with @PayloadRoot. These methods will define the actual services I am offering. I can still define services without this bean, but that’d mean I have to specify this endpoint manually in my Spring configuration. I’m lazy by nature, so annotating the class should be sufficient as far as I am concerned.
Next up is the marshaller. I’m configuring a custom ClasspathScanningJAXB2Marshaller, which I built myself. It uses the Spring classpath scanning mechanism to look for annotated JAXB2 beans. They then are added to a list of classes that are XML roots. Why use classpath scanning? The default implementation supplied by Spring requires you to manually specify any class that you want to be able to marshal. Again, I hate having to annotate the class AND register it in my configuration so I’m just going to automatically discover and register them.
/**
* JAXBMarshaller extension that uses Spring API to autodetect and autowire
* JAXB2 XML root elements. Saves the hassle of having to add them to the Spring
* configuration manually.
*
* Error messages returned by Spring-WS aren't too clear, so welcome addition.
*
* @author Jarno
*
*/
public class ClasspathScanningJAXB2Marshaller extends Jaxb2Marshaller {
private String basePackage;
private Logger logger = Logger
.getLogger(ClasspathScanningJAXB2Marshaller.class);
/**
* Constructor. Applies Spring specific API in order to get the RootNode
* annotated classes for JAXB serialization.
*/
@PostConstruct
public void init() {
logger.info("--- Attempting to scan classpath for JAXB2 annotated elements ---");
this.setClassesToBeBound(getXMLRootClasses());
logger.info("--- Scan complete ---");
}
/**
* Uses Spring classpath scanning implementation to scan for annotated
* classes. Classes that have the specific JAXB annotation for rootnodes
* will be added to the Class [].
*
* @return Class array of classes that are annotated as rootnodes
*/
public final Class<?>[] getXMLRootClasses() {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(
false);
scanner.addIncludeFilter(new AnnotationTypeFilter(XmlRootElement.class));
try {
Set<BeanDefinition> components = scanner
.findCandidateComponents(basePackage);
Class<?>[] result = new Class[components.size()];
int i = 0;
for (BeanDefinition bd : components) {
result[i] = this.getClass().getClassLoader().loadClass(bd.getBeanClassName());
logger.info("Found class " + bd.getBeanClassName());
i++;
}
return result;
}
// Not gonna happen, we are scanning - not guessing
catch (ClassNotFoundException cnfe) {
return new Class<?>[0];
}
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
}
I just extended the class and am using lifecycle callbacks to find out when Spring has completed initialisation. I also added a basePackage property, which defines the base package to start scanning in. As you can see in the Spring configuration, I supplied it with a value. The package org.walgemoed will be scanned for JAXB2 elements that are annotated as XmlRootNodes.
The third bit of configuration is the Spring genericMarshallingEndpointAdapter. This little beast will wire itself using my newly created marshaller and automatically try to marshal and unmarshal any XML I throw at it. As I have done my very best to auto-register the XmlRootNodes, I think this will work out quite nicely. I’m also adding the XSD to the configuration. I’m not using that now, but you can use the validating property on the bean configuration to have Spring validate the XML against the XSD. This is pretty neat as it will generate specific messages on why your XML isn’t correctly formed.
The final bit of configuration will allow me to automatically generate a WSDL for my services. I’m all for that, as I don’t want to do that manually. There are a couple of settings that are important. First of all, a Soap1.1DefinitionBuilder is used. This will generate a Soap1.1 specific WSDL. This will meet my needs, but if you are looking for Soap 1.2 this is also supported. Not gonna spell it out for ya though
. The definition generator takes a few property settings, schema, portTypeName and locationURI. The schema defines the contract for the service, so I’m going to go ahead and add the exact same schema as I did for my marshaller. They should match, right? The locationURI specifies where the service will be deployed. As I’ve configured my servlet to be listening for requests on /services/ I’m using that setting here. Finally, I’m going to specify the name for my services by specifying the portType.
After doing this, and after finishing my configuration the WSDL should be accessible via: http://<server>:<port>/<context-root/services/my-service.wsdl
XSD generation
I still can’t deploy my app though, as I don’t have the XML structure yet. I don’t want that job to be a manual activity, as I don’t want to re-generate my XSD every time the signature of my XML changes. At least, not during development. Good thing I’m using JAXB2, as there is a plugin that will automatically generate the XSD for me, based on the JAXB2 annotations on my classes! I’ve added the plugin to my application and configured it to execute this generation every time I build my project.
<!-- Plugin mojo that can be used to generate XSD files before deployment. -->
<plugin>
<groupId>com.sun.tools.jxc.maven2</groupId>
<artifactId>maven-jaxb-schemagen-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<destdir>${project.build.directory}/schemas</destdir>
<srcdir>${project.build.sourceDirectory}/org/walgemoed/</srcdir>
<verbose>true</verbose>
</configuration>
</plugin>
This will generate a schema file in the target/schemas folder for my maven2 project. All I need to do now is include it in my application. I used to antrun plugin for maven to do this, but there are many other ways. As you can see, I’m also changing the name. There is another way to do this as well, but hey, this works for me.
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy file="${project.build.directory}/org/walgemoed/schema1.xsd"
tofile="${project.build.outputDirectory}/my-service.xsd" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
Done!
Well, almost. Configuration is done, yes. I’ve set up XSD generation, rigged automatic marshalling with JAXB2, added scanning for root elements, set up the servlet AND pimped my build process. What could possibly be left?
Defining a service: EchoService
Right, I didn’t build a service yet. Usually a service consists of a request and an response. I like this approach so I’m going to be using ServiceRequest and ServiceResponse classes for my service. The request will contain a parameter called echo, and the response is going to echo that same parameter three times. This is a very useful service indeed.
Here’s the request:
@XmlRootElement(namespace = NameSpaceConstant.namespace)
@XmlType(namespace = NameSpaceConstant.namespace)
public class EchoRequest {
private String echo;
@XmlElement(namespace = NameSpaceConstant.namespace, required = true)
public String getEcho() {
return echo;
}
public void setEcho(String echo) {
this.echo = echo;
}
}
And here’s the response:
@XmlRootElement(namespace = NameSpaceConstant.namespace)
@XmlType(namespace = NameSpaceConstant.namespace)
public class EchoResponse {
private String response;
@XmlElement(namespace = NameSpaceConstant.namespace, required = true)
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
}
Finally, here’s the endpoint for this service:
@PayloadRoot(localPart = "echoRequest", namespace = NameSpaceConstant.namespace)
public EchoResponse echoServiceEndpoint(EchoRequest request) {
EchoResponse response = new EchoResponse();
response.setResponse(request.getEcho() + request.getEcho() + request.getEcho());
return response;
}
Build, deploy and run your application. If everything was configured correctly, and XSD should be generated based on your request and response classes. After generation, this XSD will be packaged in the deployment and wired to Spring-WS and its marshallers and WSDL generators. Open up the WSDL in a Soap testing tool (such as SoapUI) and fire up a request! If all goes well, you should get an echo of your original inputstring. As you can see, your service is completely written in Java, which means no time spent on manually generating XSD’s, WSDL’s and stuff like that. I really like this approach, it saves me a lot of time during development. Hope this helps any of you guys out there that also don’t like spending too much time on XSD’s and WSDL’s.
Spring now provides this feature inbuilt :
com.yourpackage.schema contains all Schema Classes to be bound .
Also , you can use instead of defining a bean for GenericMarshallingMethodEndpointAdatper.
Hi Shailendra! Thanks for the input.
I have to say that it’s not the same. Take a look at the documentation, listing 8.5.2 to be exact (here). If you take a close look you’ll see two specific options for configuration. One using the classesToBeBound property, and one using the contextPath property. As far as I can see, you’re talking about the latter one.
The problem with this is that the latter option doesn’t do what I want! It sets up the marshaller using the package name and then JAXB starts looking for an ObjectFactory.class or a jaxb.index file at that specific location. It didn’t want the extra hassle of having to create a factory class and having to add factory methods for all my elements. I also didn’t want to start using a jaxb.index file containing the classnames of all root element classes as it involves extra maintenance (generating the file and adding it to the classpath). I decided to keep the solution as simple as possible utilizing Spring’s powerful mechanism for sniffing out annotations on classes within the classpath and automatically setting them on the marshaller, therefore not having to do any generating/copying.
Also, scanning works recursively. The elements do not have to reside in that exact package. Using contextPath you will have to specify an entry for each classpath location that contains a jaxb.index file (or ObjectFactory). I prefer using my approach where I can just specify the root classpath of my elements and watch from a distance as Spring finds, wires and configures them withou me having to do anything else in terms of JAXB2 config – even if they do not reside in the exact location specified by the basePackage property.
You are also right about using the namespace, it makes defining a GenericMarshallingMethodEndpointAdapter obsolete. But as I am using a custom version for the marshaller, I have to add the configuration for the endpointmarshaller. No way around that, I’m afraid
Thanks for the comment!
Do u have some idea on new features introduced in Spring-WS 2.0 RC1 release?
It would be valuable if you could come up with a blog entry on that.
This marshaller is really nice, addressing as it does a major annoyance with the “official” offering. Thanks for sharing.