Sunday, September 18, 2011

RESTication of Openxava app and Vaadin client

Article under construction

The purpose of this article is to provide REST interface for Openxava (OX) application.
It shows how-to
  • integrate jersey engine into the OX build
  • extends the JPA2 entities with JAXB and JAXRS annotations
  • write a little UC with methods that returns xml/json message
  • test it
  • Make a sample Vaadin client application for those REST services
To illustrate this, I will take an Openxava application generated by minuteproject (http://minuteproject.blogspot.com/2011/09/sql-select-to-web-application-in-couple.html)

Integrate Jersey engine in OX build

Jersey lib integration
Add the following libraries in your project /web/WEB-INF/lib
  • jsr305-1.3.2.jar
  • jersey-core-1.9-ea03.jar
  • jersey-server-1.9-ea03.jar
  • asm-3.1.jar (although asm.jar is shipped with OX this version is to used)
Here the dependency is on jersey-server-1.9-ea03

In your OX project add the file servlets.xml into /web/WEB-INF

   <servlet>
<servlet-name>petshop Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
<param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>my.cool.report</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>petshop Jersey Web Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

Openxava build takes what you define in servlets.xml file and insert it in the web.xml
The snippet above specifies the following:
  • Jersey engine will parse the package for JAXRS annotation in the package "my.cool.report"
  • the context path used for REST will be /rest/*
JAXB & JAXRS integration

Add Jaxb and Jaxrs annotation in OX jpa2 entities

Class MyConferenceCoolReport
package my.cool.report.conference.domain.report;

import java.sql.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;

import javax.persistence.*;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.xml.bind.annotation.*;

import my.cool.report.conference.dto.report.*;

import org.openxava.annotations.*;
import org.openxava.jpa.*;


/**
*
*

Title: MyConferenceCoolReport


*
*

Description: Domain Object describing a MyConferenceCoolReport entity


*
*/
@Entity (name="MyConferenceCoolReport")
@Table (name="my_conference_cool_report")
@Views({
@View(
name="base",
members=
""
+ "firstName ; "
+ "lastName ; "
+ "email ; "
+ "status ; "
+ "addressStreet1 ; "
+ "addressStreet2 ; "
+ "countryCode ; "
+ "conferenceName ; "
+ "conferenceBeginDate ; "
+ "conferenceEndDate ; "
),
@View(
name="Create",
extendsView="base"
),
@View(
name="Update",
extendsView="base",
members=
""
),
@View(extendsView="base",
members=
""
),
@View(name="myConferenceCoolReportDEFAULT_VIEW",
members=
" id ;"
+ "firstName ; "
+ "lastName ; "
+ "email ; "
+ "status ; "
+ "addressStreet1 ; "
+ "addressStreet2 ; "
+ "countryCode ; "
+ "conferenceName ; "
+ "conferenceBeginDate ; "
+ "conferenceEndDate ; "
)
})

@Tabs({
@Tab(
properties=
" firstName "
+", lastName "
+", email "
+", status "
+", addressStreet1 "
+", addressStreet2 "
+", countryCode "
+", conferenceName "
+", conferenceBeginDate "
+", conferenceEndDate "
)
,
@Tab(
name = "MyConferenceCoolReportTab",
properties=
" firstName "
+", lastName "
+", email "
+", status "
+", addressStreet1 "
+", addressStreet2 "
+", countryCode "
+", conferenceName "
+", conferenceBeginDate "
+", conferenceEndDate "
)
,
@Tab(
name = "MyConferenceCoolReportTabWithRef",
properties=
" firstName "
+", lastName "
+", email "
+", status "
+", addressStreet1 "
+", addressStreet2 "
+", countryCode "
+", conferenceName "
+", conferenceBeginDate "
+", conferenceEndDate "
)
})
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement
@Path ("/myreports")
public class MyConferenceCoolReport {

@Hidden @Id @Column(name="id" )
private Long id;

@Column(name="first_name", length=255, nullable=false, unique=false)
@Required
private String firstName;
@Column(name="last_name", length=255, nullable=false, unique=false)
@Required
private String lastName;
@Column(name="email", length=255, nullable=false, unique=false)
@Required
private String email;
@Column(name="status", length=45, nullable=false, unique=false)
@Required
private String status;
@Column(name="address_street1", length=255, nullable=true, unique=false)
private String addressStreet1;
@Column(name="address_street2", length=255, nullable=true, unique=false)
private String addressStreet2;
@Column(name="country_code", length=45, nullable=false, unique=false)
@Required
private String countryCode;
@Column(name="conference_name", length=255, nullable=false, unique=false)
@Required
private String conferenceName;
@Column(name="conference_begin_date", nullable=true, unique=false)
private Date conferenceBeginDate;
@Column(name="conference_end_date", nullable=true, unique=false)
private Date conferenceEndDate;

/**
* Default constructor
*/
public MyConferenceCoolReport() {
}

public Long getId() {
return id;
}

public void setId (Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName (String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}

public void setLastName (String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}

public void setEmail (String email) {
this.email = email;
}
public String getStatus() {
return status;
}

public void setStatus (String status) {
this.status = status;
}
public String getAddressStreet1() {
return addressStreet1;
}

public void setAddressStreet1 (String addressStreet1) {
this.addressStreet1 = addressStreet1;
}
public String getAddressStreet2() {
return addressStreet2;
}

public void setAddressStreet2 (String addressStreet2) {
this.addressStreet2 = addressStreet2;
}
public String getCountryCode() {
return countryCode;
}

public void setCountryCode (String countryCode) {
this.countryCode = countryCode;
}
public String getConferenceName() {
return conferenceName;
}

public void setConferenceName (String conferenceName) {
this.conferenceName = conferenceName;
}
public Date getConferenceBeginDate() {
return conferenceBeginDate;
}

public void setConferenceBeginDate (Date conferenceBeginDate) {
this.conferenceBeginDate = conferenceBeginDate;
}
public Date getConferenceEndDate() {
return conferenceEndDate;
}

public void setConferenceEndDate (Date conferenceEndDate) {
this.conferenceEndDate = conferenceEndDate;
}

@Transient
@GET
@Path("{reportId}")
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public MyConferenceCoolReport findById (@PathParam ("reportId") Long reportId) {
EntityManager em = XPersistence.getManager();
MyConferenceCoolReport p = em.find(MyConferenceCoolReport.class, reportId);
return p;
}

@Transient
@GET
@QueryParam("{countryCode}")
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public MyConfReports find (@QueryParam ("countryCode") String countryCode) {
String q = null;
EntityManager em = XPersistence.getManager();
if (countryCode!=null) {
q = "select m from MyConferenceCoolReport m where countryCode = '"+countryCode+"'";
} else
q = "select m from MyConferenceCoolReport m ";
Query query = em.createQuery(q);
List<myconferencecoolreport> l = query.getResultList();
MyConfReports m = new MyConfReports();
m.setMyCoolReport(l);
return m;
}

}

Annotation of entities
The previous snippet indicates that entity MyConferenceCoolReport contains a transient method (findById) use for REST GET operation and using the param reportId to load an entity. It can be returned either as Xml or in a Json format.
It contains also a method find that takes a parameter a country code and return a DTO based on the class MyConfReports

package my.cool.report.conference.dto.report;
package my.cool.report.conference.dto.report;

import java.util.*;

import javax.xml.bind.annotation.*;

import my.cool.report.conference.domain.report.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
@XmlRootElement
public class MyConfReports {

private List<MyConferenceCoolReport> myCoolReport;

public MyConfReports () {
}

public List<MyConferenceCoolReport> getMyCoolReport() {
return myCoolReport;
}

public void setMyCoolReport(List<MyConferenceCoolReport> myCoolReport) {
this.myCoolReport = myCoolReport;
}

}
Deploy
Use the ant build on your project.
Remark: do not forget when using openxava with eclipse to set "Build Automatically" on the "Project" tab.

Testing
Via the browser type
http://localhost:8080/conference/rest/myreports?countryCode=BE to see all the


http://localhost:8080/conference/rest/myreports/ it returns in an XML format.




Vaadin app client
//todo

Conclusion
//todo