Tag Archives: Spring

Spring Transaction Gotcha

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

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 = &quot;product&quot;, propOrder = {
    &quot;code&quot;,
    &quot;price&quot;,
    &quot;description&quot;
})
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 = &quot;http://www.springwebservices.org/product/schema/beans&quot;;
    public final static String GET_PERSONS_REQUEST = &quot;get-product-request&quot;;

    /**
     *
     * @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(&quot;Code1&quot;);
        product.setPrice(100.00);
        product.setDescription(&quot;test&quot;);
        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