Tuesday, July 17, 2012

GWT with JAX-RS (and JPA) Part 3

Come unto me all ye who are weary of heavy-laden clients and I will give you REST.

Continued from GWT with JAX-RS (aka RPC/REST) Part 2
OK, the cast of the technology mix is
  • JAX-RS (either Resteasy or Jersey on the server side)
  • JAX-RS + GWT = RestyGWT on the client-side
  • JPA on the server-side
  • JAXB over JAX-RS on both GWT client and server-side.
  • Jackson JSON processor on server-side.

I feel those acronyms are intimidating, but they are actually not difficult to implement. If you could understand the intricacies of GWT-RPC, you should have no problem understanding the soup mix above.

JPA remains to be the most difficult part of the mix to learn/master (most difficult but not difficult). Mastering GWT is actually the more difficult part than JPA. However, if you have no desire for your server to connect to a JDBC-enabled database, you do not need to concern with JPA.

This is your JAX-RS POJO:
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement (name="employee")
    public class Employee
    implements Serializable {
      @XmlAttribute
      private Long eid = null;
      @XmlAttribute
      private String name = null;
    }
This is the same POJO, if your app is obsessed with the order of appearance:
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement (name="employee")
    @XmlType(propOrder = {
      "eid",
      "name"
    }
    public class Employee implements Serializable {
      @XmlAttribute
      private Long eid = null;
      @XmlAttribute
      private String name = null;
    }
This is the POJO with public getters/setters (generated by Eclipse) so to enable RestyGWT to operate on it. Hoever, RestyGWT would not need the public getters/setters if the fields are public instead of private.
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement (name="employee")
    @XmlType(propOrder = {
      "eid",
      "name"
    }
    public class Employee
    implements Serializable {
      @XmlAttribute
      private Long eid = null;
      @XmlAttribute
      private String name = null;
    
      public Long getEid() {
        return eid;
      }
      public void setEid (Long  eid ) {
        this. eid = eid ;
      }
      public String getName () {
        return  name ;
      }
    
      public void setName (String  name ) {
        this. name = name ;
      }
    }
And finally, this is your Three-in-one POJO to accommodate JPA.
    @Entity
    @Table(name="Employee", schema="bombay")
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement (name="employee")
    @XmlType(propOrder = {
      "eid",
      "name"
    }
    public class Employee
    implements Serializable {
      @XmlAttribute
      @Id
      private Long eid = null;
      @XmlAttribute
      private String name = null;
    
      public Long getEid() {
        return eid;
      }
      public void setEid (Long  eid ) {
        this. eid = eid ;
      }
      public String getName () {
        return  name ;
      }
      public void setName (String  name ) {
        this. name = name ;
      }
    }
Let's create another POJO:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement (name="employees")
public class EmployeeList {

  @XmlElement(name="enployee")
 private List<Employee> employees;
 
 public List<Employee> getEmployees() {
  if (employees==null)
   employees = new ArrayList<Employee>();
  
  return employees;
 }

 public void setEmployees(List<Employee> employees) {
  this.employees = employees;
 }
}
But, WHERE's THE JSON? JSON output will be borrowing the JAXB XML annotation. The following is the server side service API interface.
    @Path("/employee")
    @Produces({"application/xml", "application/json"})
    public interface EmployeeServiceAPI {
       /**
       * Find all employee ids with the given name
       */
      @GET
      @Path("/eids/name")
      LongList getEmployeeIdsWithName(@QueryParam ("name")String name) ;
      
      
      /**
       * Get employee wit Id
       */
      @GET
      @Path("/employee/{id}")
      Employee getEmployeeWithId(@PathParam ("eid")Long eid) ;
      
      /**
       * Get project list for an employee
       */
      @POST
      @Path("/projects/employee")
      ProjectList getProjects(Employee employee) ;
    }
And this is the async service API on the GWT Client.
    @Path("/employee")
    @Consumes({"application/json"}) // put in a http header to tell server to return me JSON
    public interface EmployeeServiceAPI {
      
      /**
       * Find all employee ids with the given name
       */
      @GET
      @Path("/eids/name")
      LongList getEmployeeIdsWithName(@QueryParam ("name")String name, MethodCallback  <LongList  > callback) ;
      
      /**
       * Get employee wit Id
       */
      @GET
      @Path("/employee/{id}")
      Employee getEmployeeWithId(@PathParam ("eid")Long eid, MethodCallback  <Employee  > callback) ;
      
      /**
       * Get project list for an employee
       */
      @POST
      @Path("/projects/employee")
      ProjectList getProjects(Employee employee, MethodCallback  <ProjectList  > callback) ;
    }
The server-side implementation to access the database.
    public class EmployeeServiceAPIImpl
    implements EmployeeServiceAPI {
      
      @Override
      public Employee getEmployeeIdsWithName(Long eid) {
        Employee emp = new Employee();
        
        EntityManager em = null;
        try {
          em = EMF.get().createEntityManager();
          Query q = em.createQuery(
            "SELECT n FROM Employee n WHERE n.status = 'active' AND n.eid = ?1"
          ).setParameter(1, eid);
    
          Employee emp = q.getSingleResult();
          return emp;
        }
        
        finally{
          if (em!=null)
            em.close();
        }
      }
    
      ......
    }
This is how your GWT client will call the async REST service:
  public class EmployeePresenter{

    static public EmployeeAsyncServiceAPI enpServiceAPI = GWT.create(EmployeeAsyncServiceAPI.class);
    
    public void search(Long eid) {
      enpServiceAPI.getEmployeeIdsWithName(eid, new EmployeeCallBack());
    }

    public class EmployeeCallBack
    implements MethodCallback{

      @Override
      public void onFailure(Method method, Throwable exception) {
        Window.alert("Error accessing data");
      }

      @Override
      public void onSuccess(Method method, Employee response) {
        EmployeePresenter.this.view.setEmployeeRecords(
          response.getEmployee());
        if (response.getEmployee().size()==0) {
          Window.alert("No records found");
        }
      }
    }
  }
Let's say function search(Long eid) got called as
  search(12529);
RestyGWT code generated by GWT.create() will send this request over the URL
/employee/employee/12529
RestyGWT would find the phrase in the async service API
  @Consumes({"application/json"})
and proceeds to place a header in the HTTP request
  Accepts: application/json
When the RestEasy Listener intercepts the request, it would search for the class implementing EmployeeServiceAPI and finds EmployeeServiceAPIImpl. RestEasy loads that class which proceeds to service the request.

Therefore, you could use the browser to debug your web service independent of the GWT client.

You could use python or PHP to poke the server too. May be, your QA hates Java and loves PHP - so he/she could write unit tests using PHP.

Furthermore, now, you do not have to bother with SmartGWT XML or JSON datasource and you can take full control over your POJO datasource. It's really frustrating to have to take lots of time going thro nooks and corners whenever you need to tweak your datasource a lil' bit. Now you don't have to.

Here is your web.xml to tell RestEasy to do its strut.
<web-app>
  <display-name>Employee Service</display-name>

  <!-- Auto scan REST service -->
  <context-param>
    <param-name>resteasy.scan</param-name>
    <param-value>true</param-value>
  </context-param>

  <!-- this need same with resteasy servlet url-pattern -->
  <context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/CurryMuttonOilDrilling/</param-value>
  </context-param>

  <listener>
    <listener-class>
      org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
    </listener-class>
  </listener>

  <servlet>
    <servlet-name>resteasy-servlet</servlet-name>
    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>resteasy-servlet</servlet-name>
    <url-pattern>/CurryMuttonOilDrilling/*</url-pattern>
  </servlet-mapping>

</web-app>

Therefore, according to the web.xml, the actual URL path sent to the server is:


/CurryMuttonOilDrilling/employee/employee/12529

And notice the presence of Jackson json provider in the following maven dependency?
  <properties>
    <resteasy.version>2.3.4.Final</resteasy.version>
    <gwt.version>2.4.0</gwt.version>
  </properties>

  <repositories>
    <repository>
      <id>fusesource-snapshots</id>
      <name>Fusesource Snapshots</name>
      <url>http://repo.fusesource.com/nexus/content/repositories/snapshots</url>
      <snapshots><enabled>true</enabled></snapshots>
      <releases><enabled>false</enabled></releases>
    </repository>    
  </repositories>
  
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.0.0.GA</version>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>${gwt.version}</version>
    </dependency>
    <dependency>
        <groupId>com.sencha.gxt</groupId>
        <artifactId>gxt</artifactId>
        <version>2.2.5-gwt22</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/lib/GWT/gxt-2.2.5-gwt22.jar</systemPath>
    </dependency>
    <dependency>
      <groupId>com.googlecode.mvp4g</groupId>
      <artifactId>mvp4g</artifactId>
      <version>1.4.0</version>
    </dependency>
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxrs</artifactId>
      <version>${resteasy.version}</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxb-provider</artifactId>
      <version>${resteasy.version}</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jackson-provider</artifactId>
        <version>${resteasy.version}</version>
    </dependency>
    <dependency>
      <groupId>org.fusesource.restygwt</groupId>
      <artifactId>restygwt</artifactId>
      <version>1.3-SNAPSHOT</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>3.6.10.Final</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>4.3.0.Final</version>
    </dependency>
    
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava-gwt</artifactId>
      <version>13.0-rc1</version>
    </dependency>
            <!--dependency> <groupId>com.sun.faces</groupId> <artifactId>mojarra-jsf-api</artifactId> 
      <version>2.0.0-b04</version> </dependency> <dependency> <groupId>com.sun.faces</groupId> 
      <artifactId>mojarra-jsf-impl</artifactId> <version>2.0.0-b04</version> </dependency -->

20 comments:

  1. Replies
    1. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Java developer learn from Java Training in Chennai. or learn thru Java Online Training India . Nowadays Java has tons of job opportunities on various vertical industry.

      Delete
  2. La Nike Air Max Mujer se ha convertido en una locura internacional durante mucho tiempo.
    En los últimos tiempos, muchas marcas han lanzado su colección de Camisetas Nba 2018.
    Hay tanto camisetas caras y camisetas baratas, especialmente la Camisetas Nba Baratas.
    Con el número creciente de fanáticos locos, la Camisetas Nba no solo está pensada para los jugadores.
    Las tiendas de deportes pueden querer Comprar Camisetas De Futbol.
    Para las tiendas, puede comprar Camisetas De Futbol Baratas de China y otros países a bajo precio.
    Estos Camisetas De Futbol Baratas están hechos a medida y diseñados para dar el mejor ajuste.
    La mejor forma de Comprar Camisetas De Futbol es buscar en nuestra auténtica tienda en línea.

    ReplyDelete
  3. Uno de los estilos más populares hechos por mk hoy es el Bolsos Michael Kors Rebajas. El Bolsos Michael Kors Baratos es un estilo clásico y funcional que las mujeres aman. Bolsas Michael Kors Outlet de materiales de calidad con artesanía detallada. Con todas estas ventajas, no es de extrañar que los bolsos michael kors baratos sean tan populares. Este bolsos michael kors rebajas es un bolso estilo embrague que es más tradicional en su diseño.

    Denna MK Väska kan bära böcker om du är en student och alla dina hemma arbeten om du har gått in i arbetslivet. Michael Kors Väska Rea gör också en påse stil väska och en swing stil. Väskor Michael Kors Rea har utökat sin linje för att inkludera en mängd tillbehör som inkluderar plånböcker, nyckelkedjor, kortväskor, checkbooköverdrag och kameratäckar. michael kors väska rea erbjuder också en fransk handväska och mini plånbok. michael kors väska har blivit känd som en kvalitetsdesigner som tillverkar en kvalitetsprodukt.

    Posséder un Longchamp Soldes Destockage n'est pas simplement une déclaration de mode. Toute femme intéressée par la qualité, la fonctionnalité et l’accessibilité financière devrait envisager un Sac Longchamp Bandouliere. Sac A Main Pas Cher, l’une des gammes de sacs à main et d’accessoires les plus populaires sur le marché à ce jour, propose une vaste gamme de produits longchamp soldes destockage. Puisque longchamp est si populaire, les sac longchamp pas cher sont souvent copiés.

    ReplyDelete
  4. Ray Ban Sunglasses Sale Uk are prepping for that fashionable holiday.
    Ray Ban Sale Uk are in the savvy eye of the stylish beholder.
    Ray Ban Sunglasses Sale Uk are much cheaper than you would pay in a regular store.
    Sunglasses UK meant for women are slightly different from men' varieties, so when you are choosing sun glasses for the women, you should keep in mind certain factors.
    Oakley Sunglasses Sale are very functional and known as spy sunglasses, because they are meant for recording videos and images.
    Cheap Oakley Sunglasses also offer polarization feature of the lens.

    ReplyDelete
  5. Michael Kors Outlet Online was a huge success there and became very well known. The designer Kors then branched out into accessories including Michael Kors Handbags Clearance. He never lost focus on his American chic styling. Some of his most notable collections include the Michael Kors Totes, Michael Kors satchel and the Designer MK Outlet and Michael Kors Outlet Online Store.

    No one has to know that they are cheap Ray Ban Sale UK and no one will know unless you tell them. Each of the cheap designer Ray Ban Sunglasses Sale UK carries the signature brand on the lens to let you know that you do have authentic designer Cheap Ray Ban Sunglasses UK.

    tags: Cheap Oakley Sunglasses UK,MK Outlet

    ReplyDelete
  6. Ce sac cartable Michael Kors est si universel qu'il pourrait plaire à tout le monde. Dans cet article, je vais essayer de passer en revue les caractéristiques principales et les éléments qui distinguent ce sac à main des autres.

    tags:Bolsos Michael Kors Rebajas,Bolsos Michael Kors Baratos,Bolsos Michael Kors El Corte Ingles

    L’extérieur de ce fourre-tout Michael Kors est un cuir métallisé de laiton froncé et froncé. Si vous connaissez les sacs à main Michael Kors, vous savez que ce cuir sera doux et souple. Les deux grandes poignées supérieures sont attachées au sac avec quelques centimètres de chaînes dorées, puis les moitiés supérieures des bretelles présentent le même cuir couleur laiton.

    tags:Bolsos Michael Kors Baratos,Michael Kors Örhängen

    La diversité des couleurs et la taille utilisable de ce sac le rendent parfait pour un usage quotidien. Les poches intérieures ne manquent pas pour aider à garder les choses en ordre. Il y a des endroits parfaits pour votre téléphone ou votre Blackberry. Le zip top empêche les malfaiteurs de saisir votre sac dans un bar et aide également à contenir tout ce qui se trouve à l'intérieur lorsque vous courez pour créer ce train.

    tags:Windguru Longchamps,Longchamp Soldes Destockage,Pronote College Longchamp

    ReplyDelete
  7. It is amazing and wonderful to visit your site.Thanks for sharing this information,this is useful to me...
    Best Devops online Training
    Online DevOps Certification Course - Gangboard

    ReplyDelete
  8. Thinking how to make money? Come to us and win now good slot online Play and win always and with us.

    ReplyDelete
  9. Do not think! Just come with us to BGAOC and play with us. best casino games come to us and win soon.

    ReplyDelete

  10. It seems you are so busy in last month. The detail you shared about your work and it is really impressive that's why i am waiting for your post because i get the new ideas over here and you really write so well.

    Selenium training in Chennai

    ReplyDelete