Intro

REST-JEE is minuteproject track to quickly have a 'RESTified' backend on top of a JPA2 layer.
REST-JEE REST service implementation is based on the Jersey implementation.
This page goes thru:

You can generate the code against you DB model via minuteproject console or through configuration. For that, download MinuteProjectlast version.

For the rest of the page will deal with the code generated from the demo application.
You can have the resulting source if you download demo-src-generated-0.5.7.zip
Or you can generate by running /demo/config/all-demos.(cmd/sh), see Demos.

Operating principle

This diagram resumes how MinuteProject works in the context of code generation for REST application that is ultimately deployed on Glassfish.
The glassfish server used is 3.1.1.b08 bundled with Netbeans 7.0.1 RC1

REST-gf.png


Configuration

Here is the main configuration dealing the model enrichment and the load of the REST target templates.
<!DOCTYPE root>
<generator-config>
    <configuration>
        <model name="petshop" version="1.0" package-root="net.sf.mp.demo">
            <data-model>
                <driver groupId="org.apache.derby" artifactId="derby" version="10.8.1.2"/>
                <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>
                <primaryKeyPolicy oneGlobal="true">
                    <primaryKeyPolicyPattern name="autoincrementPattern"/>
                </primaryKeyPolicy>
            </data-model>
            <business-model>
                <generation-condition>
                    <condition type="exclude" startsWith="DUAL"></condition>
                    <condition type="exclude" startsWith="ID_GEN"></condition>
                    <condition type="exclude" startsWith="SEQUENCE"></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>
                      <entity-naming-convention type="apply-strip-table-name-prefix" pattern-to-strip="SYS,FIN"/>
                      <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="TAG_ITEM" master-relationship-field="ITEMID"/>
                    <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-JEE"/>
    </configuration>
</generator-config>
The main oustanding points dealing with REST are:
  • <property name="use-temporal" value="true"/>
    • indicates to use Temporal annotation instead of java.sql.Timestamp/Date. It is useful since Timestamp does not have a no-arg constructor.
  • <entity name="TAG_ITEM" master-relationship-field="ITEMID"/>
    • The TAG_ITEM table is use for a ManyToMany relationship and nothing is generated as a JPA2 entity.
    • Meanwhile when it is relevant, setting the master of the relationship indicates the owner (entity) of the information.
      • Here ITEM table is the owner
  • <property name="add-xmlbinding" value="true">
    • Means that JPA2 template must add JAXB annotations
  • <target name="REST" ...
    • Indicates the REST templates to use and with version of jersey to use in the maven build


Generation

Prequisites

Have Java 6 setup.
Download MinuteProjectlast version.

Ensure that you have the petshop database running.
/sample/start-petshop-database.(cmd/sh)

Run demo-REST-JEE.(cmd/sh) in /demo/config.

Resulting artifacts


3 maven pom project structure are generated
  • One global for JPA2 and REST
  • One for JPA2 (see JPA2 for details regarding jpa2 annotations, see below for some explanation about jaxb annotations)
  • One for REST (jersey implementation) whose structure open as a maven netbean project is the following

REST-jersey-mvn-structure-netbean.png

The maven REST project contains the following artifacts.
  • web.xml with Jersey integration
  • stateless EJB3
  • REST resources for CRUD
  • pom.xml for REST with Jersey integration

Application Code

Inside the rest package here is the same code generated for the AddressResouce

AddressResouce

@Path ("/rest/xml/addresses")
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@RequestScoped
public class AddressResource  {
 
    @Inject
    private AddressDaoEJB addressDaoEJB;
 
    public List<Address> findAll (Address address) {
        return addressDaoEJB.findAll(address);
    }
 
    @GET
    @Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public List<Address> findAll () {
        return addressDaoEJB.findAll();
    }
 
    @GET
    @Path("{addressid}")
    @Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Address findById (@PathParam ("addressid") java.lang.Integer addressid) {
        return addressDaoEJB.findById(addressid);
    }
 
    @DELETE
    @Path("{addressid}")
    public void delete (@PathParam ("addressid") Integer addressid) {
        Address address = new Address ();
        address.setAddressid(addressid);
        addressDaoEJB.delete(address);
    }
 
    public Address create (Address address) {
        return addressDaoEJB.create(address);
    }
 
    @POST
    @Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Address create (
        @FormParam("addressid") Integer addressid,
        @FormParam("street1") String street1,
        @FormParam("street2") String street2,
        @FormParam("city") String city,
        @FormParam("state") String state,
        @FormParam("zip") String zip,
        @FormParam("latitude") java.math.BigDecimal latitude,
        @FormParam("longitude") java.math.BigDecimal longitude,
        @Context HttpServletResponse servletResponse
        ) throws IOException {
        Address _address = new Address (
           addressid,
           street1,
           street2,
           city,
           state,
           zip,
           latitude,
           longitude);
        return create(_address);
    }
 
    @PUT
    @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Address save(JAXBElement<Address> jaxbAddress) {
        Address address = jaxbAddress.getValue();
        if (address.getAddressid()!=null)
            return addressDaoEJB.update(address);
        return create(address);
    }
 
 
}
Remark:
There is a trick to thawte an 'unexpected' behavior:
The annotation inject does not refered to javax.inject.Inject (the CDI does not work on REST resource...) but to com.sun.jersey.spi.inject.Inject (It works although deprecated...)


Jaxb annotations

MyGoodItems

In the backend stack the JPA2 entities are decorated with binding annotations such as for the table ITEM (aliased as MY_GOOD_ITEMS).
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(namespace="net.sf.mp.demo.petshop.product", name = "MyGoodItem")
@XmlRootElement(namespace="net.sf.mp.demo.petshop.product")
public class MyGoodItem implements Serializable {
 
    @Id @Column(name="ITEMID" )
    @GeneratedValue(strategy = GenerationType.AUTO)
    @XmlElement
    private Integer itemid;
 
    @Column(name="NAME",  length=30, nullable=false,  unique=false)
    @XmlElement
    private String name;
    @Column(name="DESCRIPTION",  length=500, nullable=false,  unique=false)
    @XmlElement
    private String description;
    @Column(name="IMAGEURL",  length=55,  nullable=true,  unique=false)
    @XmlElement
    private String imageurl;
    @Column(name="IMAGETHUMBURL",  length=55,  nullable=true,  unique=false)
    @XmlElement
    private String imagethumburl;
    @Column(name="PRICE",   nullable=false,  unique=false)
    @XmlElement
    private java.math.BigDecimal price;
    @Column(name="TOTALSCORE",   nullable=false,  unique=false)
    @XmlElement
    private Integer totalscore;
    @Column(name="NUMBEROFVOTES",   nullable=false,  unique=false)
    @XmlElement
    private Integer numberofvotes;
    @Column(name="DISABLED",   nullable=false,  unique=false)
    @XmlElement
    private Integer disabled;
 
    @ManyToOne (fetch=FetchType.LAZY , optional=false)
    @JoinColumn(name="ADDRESS_ADDRESSID", referencedColumnName = "ADDRESSID", nullable=false,  unique=false )
    private Address addressAddressid;
 
    @Column(name="ADDRESS_ADDRESSID",  nullable=false,  unique=false, insertable=false, updatable=false)
    @XmlAttribute (name="addressAddressid")
    private Integer addressAddressid_;
 
    @ManyToOne (fetch=FetchType.LAZY , optional=false)
    @JoinColumn(name="PRODUCTID", referencedColumnName = "PRODUCTID", nullable=false,  unique=false )
    private MyGoodProduct thisIsMyProduct;
 
    @Column(name="PRODUCTID", length=10, nullable=false,  unique=false, insertable=false, updatable=false)
    @XmlAttribute (name="thisIsMyProduct")
    private String thisIsMyProduct_;
 
    @ManyToOne (fetch=FetchType.LAZY , optional=false)
    @JoinColumn(name="CONTACTINFO_CONTACTINFOID", referencedColumnName = "CONTACTINFOID", nullable=false,  unique=false )
    private Sellercontactinfo contactinfoContactinfoid;
 
    @Column(name="CONTACTINFO_CONTACTINFOID",  nullable=false,  unique=false, insertable=false, updatable=false)
    @XmlAttribute (name="contactinfoContactinfoid")
    private Integer contactinfoContactinfoid_;
 
    @XmlElement (name="tag")
    @ManyToMany
    @JoinTable(name="TAG_ITEM",
        joinColumns=@JoinColumn(name="ITEMID"),
        inverseJoinColumns=@JoinColumn(name="TAGID")
    )
    private Set <Tag> tags = new HashSet <Tag> ();
 
The remarkable points dealing with MP generated Jaxb annotations are:
  • @XmlRootElement with namespace as package name.
  • @XmlElement for each attribute of entity
  • @XmlAttribute for Foreign keys
  • For @ManyToMany
    • @XmlTransient in nothing specified in enrichement or for aggregated part (to avoid circular reference in xml)
    • @XmlElement with singular object reference for the master side of the relations specified in the enrichment by <entity name="TAG_ITEM" master-relationship-field="ITEMID"/>


Packaging-deployment

Packaging

On /output/REST/petshop run:
mvn package
It will create the
  • JPA2 backend
  • REST API

The result app war petshopRESTApp.war is in /output/REST/petshop/REST/target.

Deployment

Start glassfish (Tested on 3.1.1.b08 bundled with Netbeans 7.0.1 RC1).
Remark: With previous releases you may experience some problems when jaxb is retrieving list of list of elements in jpa2 graph.

Check jdbc/petshopDS Datasource is configured.
Deploy App:
  • by console
  • by command line asadmin deploy --force=true REST\target\petshopRESTApp.war


Testing


It will be done by the following means
  • firefox
  • soapui
  • curl

Scenario

Search, create, update and delete an address.

Search

Search address
REST-select-address-element-firefox.png

Search items (alias in enrichment by mygooditems)
REST-select-mygoodproduct-element-firefox.png

Create

Create an address using SOAPUI

REST-create-address-element-SOAPUI.png

Update

Update the address
REST-update-address-element-SOAPUI.png

Delete

Using curl
REST-delete-address-element-CURL.png





Knownledge sources


http://jersey.java.net/

http://www.verborgh.be/articles/2009/11/21/easy-restfull-jax-rs-webservices-and-extended-wadl-on-glassfish-v3-using-ant-/

http://www.vogella.de/articles/REST/article.html

http://wikis.sun.com/display/Jersey/HowToConfigureExtendedWADL

http://www.ibm.com/developerworks/web/library/wa-aj-tomcat/index.html

http://blogs.oracle.com/arungupta/entry/totd_124_using_cdi_jpa