Monthly Archives: August 2014
Spring Transaction Gotcha
August 20, 2014
Posted by on Previously a follow developer asked me to have a look at a strange transactional problem he was having with Spring. He had a class like below.
@Service public class TransactionProblem { public void entryPointToClass() { doTransactionalProcessing(); } @Transactional private void doTransactionalProcessing() { //do some db stuff } }
He was wondering why the doTransactionalProcessing was not being executed in a transaction. The reason is depending on the spring configuration. Spring can use CGLIB to subclass you service class and delegate all calls to your service class. It works something like below
public class TransactionProblemProxy extends TransactionProblem { @Override public void entryPointToClass() { //spring starts the transaction here if the method is annotated @Transactional (its not) super.entryPointToClass(); //spring commits the transaction here if the method is annotated @Transactional (its not)</pre> } }
So as you can see from the above code when we call our service class it actually calls the TransactionProblemProxy. The method is not annotated transactional so no transaction is created. The call to the private method doTransactionalProcessing is not done through spring so the annotation is ignored.The solution to the above issue is either make the doTransactionalProcessing public and call it seperately or change the spring configuration to use AspectJ.
Spring-WS Contract First Web Service Example
August 19, 2014
Posted by on This post will show you how to write contract-first Web services. Contract-first web services start with defining and creating the xsd and wsdl (the contract) before writing any java code. During this post we will create a very simple application that will expose one web service. All code for this application can be found at https://github.com/jwatters1981/spring-web-services
As stated previously when creating contract-first web services the first step is to create the contract i.e. the xsd. Below is the xsd file we will be using.
Step 1 Create the XSD
<xsd:schema xmlns="http://www.springwebservices.org/product/schema/beans" targetNamespace="http://www.springwebservices.org/product/schema/beans" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="get-product-request"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="product-response"> <xsd:complexType> <xsd:sequence> <xsd:element name="product" type="product" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:complexType name="product"> <xsd:sequence> <xsd:element name="code" type="xsd:string" /> <xsd:element name="price" type="xsd:double" /> <xsd:element name="description" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:schema>
The xsd file is pretty straight forward we have a get-product-request element which contains a string element, a product element which holds code, price and description and a product-response element which holds a list of products which will be returned from the web service method.
Step 2 Generate Java Sources
The second step is to generate java source files from the xsd, this can be done with the xjc utility which comes with java. To run this simply open up a command prompt for windows or a terminal for mac and linux and type xjc -d generated <path to xsd> for me this was xjc -d generated /Users/johnwatters/spring-web-services/src/main/resources/product.xsd
You can build this source code generation into your maven build process but for this post I used the command approach and copied the generated source into my project. You would obviously not do this in a real project.
After you run the xjc command you should see output like below
parsing a schema…
compiling a schema…
org/springwebservices/product/schema/beans/GetProductRequest.java
org/springwebservices/product/schema/beans/ObjectFactory.java
org/springwebservices/product/schema/beans/Product.java
org/springwebservices/product/schema/beans/ProductResponse.java
org/springwebservices/product/schema/beans/package-info.java
An example of some of the code generated can be seen below.
@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "product", propOrder = { "code", "price", "description" }) public class Product { @XmlElement(required = true) protected String code; protected double price; @XmlElement(required = true) protected String description;
Copy the generated source into a maven project.
Step 3 Create a Spring web service Endpoint class
Below you will see the code that makes up the web service endpoint. In order to tell spring that this is indeed and web service endpoint you need to annotate it with @Endpoint as seen below.
@Endpoint public class SampleEndpoint { public final static String NAMESPACE = "http://www.springwebservices.org/product/schema/beans"; public final static String GET_PERSONS_REQUEST = "get-product-request"; /** * * @param code * @return */ @PayloadRoot(localPart = GET_PERSONS_REQUEST, namespace = NAMESPACE) public @ResponsePayload ProductResponse getProducts(@RequestPayload GetProductRequest code) { ProductResponse productResponse = new ProductResponse(); Product product = new Product(); product.setCode("Code1"); product.setPrice(100.00); product.setDescription("test"); productResponse.getProduct().add(product); return productResponse; } }
The getProducts method is annotated with the @PayloadRoot annotation to enable spring to call the required method for the soap message. This endpoint class could potentially have many method consuming different soap messages.
Step 4 The Spring context Files
Below is the spring context file for the application.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.springwebservices" /> <bean id="productService" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition" p:portTypeName="Product" p:locationUri="/productService/" p:requestSuffix="-request" p:responseSuffix="-response"> <property name="schema"> <bean class="org.springframework.xml.xsd.SimpleXsdSchema" p:xsd="classpath:/product.xsd" /> </property> </bean> <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"> </bean> <bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter"> <description>Enables the MessageDispatchServlet to invoke methods requiring OXM marshalling. </description> <constructor-arg ref="marshaller" /> </bean> <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <property name="contextPath" value="org.springwebservices.product.schema.beans" /> <property name="schema" value="classpath:/product.xsd" /> </bean> </beans>
The important lines are line number 10 which is the component scan. This tells spring to look for any classes that are annotated, like our @Endpoint class. Line number 12 to 17 expose the wsdl. Once the application is up and running its this configuration that allows you to go to http://localhost:8080/spring-web-services/productServices.wsdl and see the wsdl.
This MarshallingMethodEndpointAdapter uses the generated source code to marshal the incoming requests.
Step 5 the web.xml
The important part of the web xml is the spring-ws. This servlet takes in our spring context file and routes the requests onto our endpoint.
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <servlet> <servlet-name>spring-ws</servlet-name> <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-ws-context.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>spring-ws</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
Step 6 The Maven pom
<?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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springwebservices</groupId> <artifactId>spring-web-services</artifactId> <version>1.1.4</version> <name>Simple Spring Web Services</name> <description>Spring web services using JaxB.</description> <properties> <org.springframework.version>3.2.3.RELEASE</org.springframework.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.ws</groupId> <artifactId>spring-ws-core</artifactId> <version>2.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.1.1.RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> <version>1.6.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> </plugin> </plugins> <pluginManagement> <plugins> <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. --> <plugin> <groupId>org.eclipse.m2e</groupId> <artifactId>lifecycle-mapping</artifactId> <version>1.0.0</version> <configuration> <lifecycleMappingMetadata> <pluginExecutions> <pluginExecution> <pluginExecutionFilter> <groupId>org.jvnet.jaxb2.maven2</groupId> <artifactId>maven-jaxb2-plugin</artifactId> <versionRange>[0.8.2,)</versionRange> <goals> <goal>generate</goal> </goals> </pluginExecutionFilter> <action> <ignore></ignore> </action> </pluginExecution> </pluginExecutions> </lifecycleMappingMetadata> </configuration> </plugin> </plugins> </pluginManagement> </build> <packaging>war</packaging> </project>
To build the project run
mvn install tomcat:run
You should be able to browse to the wsdl at http://localhost:8080/spring-web-services/productService.wsdl
You can use Soap UI to generate a soap request from the above wsdl. The url to send the soap request to is http://localhost:8080/spring-web-services/productService
You should get the below response back
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <SOAP-ENV:Body> <ns3:product-response xmlns:ns3="http://www.springwebservices.org/product/schema/beans"> <product> <code>Code1</code> <price>100.0</price> <description>test</description> </product> </ns3:product-response> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
All source available at https://github.com/jwatters1981/spring-web-services
Please let me know if you found the post useful