Spring MVC is a opensource framework from springframework handling http request.
It handles also REST request in a similar way it deals with http request from web site.

Minuteproject 4 REST-SpringMVC provides spring framework and spring-mvc with artifacts from a reverse-engineered data model.
SpringMVC generated artifacts are built on top of spring BSLA DAO stack and JPA2 track, the glue is done by spring configuration.

The generated artefacts are packaged in 4 maven projects
  • One global for JPA2, BSLA DAO, CXF REST
  • One for JPA2 (see JPA2 for details regarding jpa2 annotations, and jaxb annotations)
  • One for BSLA DAO
  • One for SpringMVC REST implementation

The dedicated artifacts for CXF are
  • At model level
    • spring application context
    • rest-servlet spring configuration
    • web.xml
  • At entity level
    • Spring MVC controller handling REST request

The DAOs, spring configuration, JPA2 and other backend aspects are generate from associated tracks.

To have a sample please check what is generated in mp-source-generated-0.7.zip+ folder rest-springmvc

Generation Step

The 'Generation step' consists of configuring MP kernel either via a configuration file or via the console.

Prequisites

Have Java 6 setup.
Download MinuteProjectlast version.
Check that your database connection is available

Generation Via the console

  1. Start the MP console (<MP_HOME>/bin/start-console.(cmd/sh). How to use the console.
  2. Fill the connection information to the datamodel
  3. Fill the model general information (model, package names, version primary key policy, scope).
  4. Choose SpringMVC target
  5. Click generate

Generation Via the configuration file and command line

An example can be found <MP_HOME>/demo/config/mp-config-REST-SpringMVC.xml
mp-config-REST-SpringMVC.xml references the shipped petshop database (<MP_HOME>/sample).

To run it, execute demo-REST-SpringMVC.(cmd/sh)


Configuration

mp-config-REST-SpringMVC.xml is the configuration holding
  • data model specific location as well as 'enrichement' feature
  • techno tracks (Spring, hibernate, JPA2, SpringMVC...) to use
<!DOCTYPE root>
<generator-config>
    <configuration>
        <conventions>
            <target-convention type="enable-updatable-code-feature" />
        </conventions>
        <model name="petshop" version="1.0" package-root="net.sf.mp.demo.jpa2">
            <data-model>
                <driver name="hsqldb" version="1.8.0.7" groupId="hsqldb" artifactId="hsqldb"></driver>
                <fileSource name="petshop" dir="c:/MinuteProject/data">
                </fileSource>
                <dataSource>
                    <driverClassName>org.hsqldb.jdbcDriver</driverClassName>
                    <url>jdbc:hsqldb:hsql://127.0.0.1:9001/petshop</url>
                    <username>sa</username>
                    <password></password>
                </dataSource>
                <!-- for Oracle and DB2 please set the schema
                <schema> </schema>
                 -->
                <primaryKeyPolicy oneGlobal="true" oneForEachTable="false">
                    <primaryKeyPolicyPattern prefix="" suffix=""
                                         name="sequencePattern" sequenceName="hibernate_sequence"/>
                </primaryKeyPolicy>
            </data-model>
            <business-model>
                <generation-condition>
                    <condition type="exclude" startsWith="DUAL"></condition>
                    <condition type="exclude" startsWith="ID_GEN"></condition>
                </generation-condition>
                <business-package default="pet">
                    <condition type="package" startsWith="PRODUCT" result="product"></condition>
                    <condition type="package" startsWith="ITEM" result="product"></condition>
                </business-package>
                <enrichment>
                   <conventions>
                      <column-naming-convention
                                        type="apply-fix-primary-key-column-name-when-no-ambiguity"
                                        default-value="ID"/>
                      <reference-naming-convention
                                        type="apply-referenced-alias-when-no-ambiguity"
                                        is-to-plurialize="true"></reference-naming-convention>
                   </conventions>
                    <package name="product">
                        <entity-group entities="PRODUCT"></entity-group>
                        <entity-group entities="ITEM"></entity-group>
                    </package>
                    <entity name="PRODUCT" alias="MY_GOOD_PRODUCT">
                    </entity>
                    <entity name="ITEM" alias="MY_GOOD_ITEM" comment="my item table">
                        <field name="PRODUCTID" alias="THIS_IS_MY_PRODUCT"
                                                comment="my product field reference"></field>
                    </entity>
                    <entity name="CATEGORY" content-type="reference-data" >
                       <semantic-reference>
                         <sql-path path="NAME"/>
                       </semantic-reference>
                    </entity>
                </enrichment>
            </business-model>
        </model>
        <targets catalog-entry="REST-SpringMVC" />
    </configuration>
</generator-config>
The main points relevant for CXF are:
Enabling updatable feature: the code containing updatable part will be handled

            <target-convention type="enable-updatable-code-feature" />

Concretely this means you can change CXF Resource artifacts generated.



Generated artifacts


Here only the artifacts SpringMVC specifics are mentionned

Structure

rest-springmvc-pom-structure.png

Detail


Resource entity


Exemple: AddressResource
package net.sf.mp.demo.jpa2.petshop.rest.pet;
 
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.io.*;
import java.sql.*;
 
import java.io.IOException;
import javax.servlet.http.*;
import java.util.List;
import java.util.ArrayList;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestParam;
 
 
import net.sf.mp.demo.jpa2.petshop.dao.face.pet.AddressDao;
import net.sf.mp.demo.jpa2.petshop.dao.face.pet.AddressExtDao;
import net.sf.mp.demo.jpa2.petshop.domain.pet.Address;
 
/**
 *
 * <p>Title: AddressResource</p>
 *
 * <p>Description: remote interface for AddressResource service </p>
 *
 */
@RequestMapping ("/rest/xml/addresses")
@Controller
public class AddressResource  {
 
    @Autowired
    @Qualifier("addressDao")
    AddressDao addressDao;
 
    @Autowired
    @Qualifier("addressExtDao")
    AddressExtDao addressExtDao;
 
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @FIND_ALL-ADDRESS@
    @RequestMapping(method = RequestMethod.GET)
    @Transactional
    @ResponseBody
    public List<Address> findAll () {
        List<Address> r = new ArrayList<Address>();
        List<Address> l = addressDao.searchPrototypeAddress(new Address());
        for (Address address : l) {
            r.add(address.flat());
        }
        return r;
    }
//MP-MANAGED-UPDATABLE-ENDING
 
//MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @FIND_BY_ID-ADDRESS@
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    @Transactional
    public Address findById (@PathVariable ("id") java.lang.Integer id) {
        Address _address = new Address ();
        _address.setId(id);
        _address = addressExtDao.getFirstAddress(_address);
        if (_address!=null) return _address.flat();
        return new Address ();
    }
//MP-MANAGED-UPDATABLE-ENDING
 
    @RequestMapping(method = RequestMethod.POST)
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
    @ResponseBody
    public Address create (
        @RequestParam("id") Integer id,
        @RequestParam("street1") String street1,
        @RequestParam("street2") String street2,
        @RequestParam("city") String city,
        @RequestParam("state") String state,
        @RequestParam("zip") String zip,
        @RequestParam("latitude") java.math.BigDecimal latitude,
        @RequestParam("longitude") java.math.BigDecimal longitude,
        HttpServletResponse servletResponse
        ) throws IOException {
        Address _address = new Address (
           id,
           street1,
           street2,
           city,
           state,
           zip,
           latitude,
           longitude);
        return save(_address);
    }
 
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
    @ResponseBody
    public String delete (@PathVariable ("id") Integer id) {
        Address address = new Address ();
        address.setId(id);
        addressDao.deleteAddress(address);
        return "OK";
    }
 
    @RequestMapping(method = RequestMethod.PUT)
    @ResponseBody
    @Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
    public Address save (
        @RequestParam("id") Integer id,
        @RequestParam("street1") String street1,
        @RequestParam("street2") String street2,
        @RequestParam("city") String city,
        @RequestParam("state") String state,
        @RequestParam("zip") String zip,
        @RequestParam("latitude") java.math.BigDecimal latitude,
        @RequestParam("longitude") java.math.BigDecimal longitude,
        HttpServletResponse servletResponse
        ) throws IOException {
        Address _address = new Address (
           id,
           street1,
           street2,
           city,
           state,
           zip,
           latitude,
           longitude);
        return save(_address);
    }
 
    public Address save (Address address) {
    addressDao.saveAddress(address);
    return address;
    }
 
}
The main points are:
  • define access path with spring annotation
  • define format
  • provide CRUD access to entities
  • use BSLA DAO

rest-servlet.xml

<?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:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
 
    <tx:annotation-driven transaction-manager="petshopTransactionManager"/>
 
    <context:component-scan base-package="net.sf.mp.demo.jpa2.petshop.rest">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
 
<!--     <context:component-scan base-package="dw.spring3.rest.controller" />
 -->    <!-- To enable @RequestMapping process on type level and method level -->
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="marshallingConverter" />
                <ref bean="jsonConverter" />
            </list>
        </property>
    </bean>
 
    <bean id="marshallingConverter"
          class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
        <constructor-arg ref="jaxbMarshaller" />
        <property name="supportedMediaTypes" value="application/xml"/>
    </bean>
 
    <bean id="jsonConverter"
          class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
        <property name="supportedMediaTypes" value="application/json" />
    </bean>
 
    <!-- Client -->
    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <property name="messageConverters">
            <list>
                <ref bean="marshallingConverter" />
                <ref bean="jsonConverter" />
            </list>
        </property>
    </bean>
 
    <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="classesToBeBound">
            <list>
                <!-- pet -->
                <value>net.sf.mp.demo.jpa2.petshop.domain.pet.Address</value>
                <value>net.sf.mp.demo.jpa2.petshop.domain.pet.Category</value>
                <value>net.sf.mp.demo.jpa2.petshop.domain.pet.Sellercontactinfo</value>
                <value>net.sf.mp.demo.jpa2.petshop.domain.pet.Tag</value>
                <value>net.sf.mp.demo.jpa2.petshop.domain.pet.Ziplocation</value>
                <!-- product -->
                <value>net.sf.mp.demo.jpa2.petshop.domain.product.MyGoodItem</value>
                <value>net.sf.mp.demo.jpa2.petshop.domain.product.MyGoodProduct</value>
 
            </list>
        </property>
    </bean>
 
</beans>

Spring application-context

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
    default-autowire="byName">
    <context:annotation-config />
    <import resource="classpath:net/sf/mp/demo/jpa2/petshop/factory/spring/spring-config-Petshop-BE-main.xml"/>
 
</beans>
 

web.xml

<?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_3_0.xsd"
    id="WebApp_ID" version="3.0">
    <display-name>rest</display-name>
 
    <!-- The context params that read by ContextLoaderListener  -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/application-context.xml
        </param-value>
    </context-param>
 
    <!-- This listener will load other application context file in addition to springweb-servlet.xml -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
 
    <servlet>
        <servlet-name>rest</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>rest</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
 
    <!-- Disables servlet container welcome file handling. Needed for compatibility
        with Servlet 3.0 and Tomcat 7.0 -->
    <welcome-file-list>
        <welcome-file />
    </welcome-file-list>
 
</web-app>

Packaging-deployment

Interventions

Tomcat dependency

There is a little trick here. The deployment is on tomcat.
Tomcat by default do not support JTA transactions.
But the resulting persistence.xml code generated with
<!--MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @PERSISTENCE-UNIT-petshop@-->
    <persistence-unit name="petshop" transaction-type="JTA">
<!--MP-MANAGED-UPDATABLE-ENDING-->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
<!--MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @DATASOURCE-petshop@-->
        <jta-data-source>jdbc/petshopDS</jta-data-source>
<!--MP-MANAGED-UPDATABLE-ENDING-->
will not work.
The code has to be altered:
  • to run for non-jta data source
  • to keep this alteration over other reverse-engineering executions.
<!--MP-MANAGED-UPDATABLE-BEGINNING-ENABLE @PERSISTENCE-UNIT-petshop@-->
    <persistence-unit name="petshop" >
<!--MP-MANAGED-UPDATABLE-ENDING-->
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
<!--MP-MANAGED-UPDATABLE-BEGINNING-ENABLE @DATASOURCE-petshop@-->
        <non-jta-data-source>jdbc/petshopDS</non-jta-data-source>
<!--MP-MANAGED-UPDATABLE-ENDING-->

BSLA dependency

Bsla jar is not deployed in maven central yet but shipped with minuteproject. It is needed by the project Bsla DAO.
Go to <MP_HOME>/target/bsla
Run install_bsla(sh/cmd)

Packaging

On <MP_HOME>/demo/output/cxf/petshop run:
mvn clean package
It will create the
  • JPA2 backend
  • BSLA DAO layer
  • REST SpringMVC implementation
The result app war petshopRestCxfApp.war is in <MP_HOME>/demo/output/cxf/petshop/rest/target.

Deployment

On tomcat

  • add connexion pool snippet
    <Resource name="jdbc/petshopDS" auth="Container" type="javax.sql.DataSource"
       maxActive="20" maxIdle="5" maxWait="10000"
       username="sa" password="" driverClassName="org.hsqldb.jdbcDriver"
       url="jdbc:hsqldb:hsql://127.0.0.1:9001/petshop"/>
  • deploy petshopRestSpringMVCApp.war

Testing

Select all

rest-springmvc-addresses.png

Select one

rest-springmvc-address-one.png

Create

//todo

Update

//todo

Delete

curl -X DELETE http://localhost:8080/petshopRestSpringMVCApp/rest/xml/addresses/152