Monday, 15 October 2012

SPRING JPA and REST Together


Spring

The Spring Framework is an open source application framework and Inversion of Control container for the Java platform.

JPA

Java Persistence API (JPA) provides POJO (Plain Old Java Object) standard and object relational mapping (OR mapping) for data persistence among applications. Persistence, which deals with storing and retrieving of application data

REST 

As described in a dissertation by Roy Fielding, REST is an "architectural style" that basically exploits the existing technology and protocols of the Web, including HTTP (Hypertext Transfer Protocol) and XML. REST is simpler to use than the well-known SOAP (Simple Object Access Protocol) approach, which requires writing or using a provided server program (to serve data) and a client program (to request data). SOAP, however, offers potentially more capability. For example, a syndicator that wanted to include up-to-date stock prices to subscribing Web sites might need to use SOAP, which allows a greater amount of program interaction between client and server.

Incorporating these technologies together is a little bit of challenge.

JPA is a specification it has many implementation.Hibernate is one of the most widely used implementation.Here we are going to use hibernate(4.x.x) as JPA implementation.
REST too has many implementaion.
We will focus mainly on Oracle Jersey and JBOSS RESTEasy.

 Jersey will work only on Glassfish  but RESTEasy will work on any Application Server.
 But we need to do some changes accordingly.

Let's see how to make these stuffs work together for a GlassFish Application Server.

Here is our applicationContext.xml:-

applicationContext.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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:flow="http://www.springframework.org/schema/webflow-config"
       xmlns:jms="http://www.springframework.org/schema/jms"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:osgi="http://www.springframework.org/schema/osgi"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
          http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd
          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
          http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
          http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-3.0.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
          http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd ">
              
     <!-- specifying the property file   -->
    <bean id="placeholderPropertiesuserdao" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:application.properties" />
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreUnresolvablePlaceholders" value="true" />
    </bean>
    
  <bean class="com.opensourzesupport
.rest.network.ProductResource" p:platformTransactionManager-ref="transactionManager" />
  
    <!-- datasource information -->
    <bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close"  >
        <property name="uRL" value="${db.jdbc.url}" />
        <property name="user" value="${db.jdbc.user}" />
        <property name="password" value="${db.jdbc.password}" />
    </bean>
    
    <context:component-scan base-package="com.opensourzesupport.model" />

    
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory"/>  
   
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >  
        <property name="persistenceUnitName" value="app_persist_unit"/>  
        <property name="dataSource" ref="dataSource"/>  
        <property name="persistenceXmlLocation" value="classpath:persistence.xml"/> 
        <property name="jpaVendorAdapter">  
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="${db.orm.showsql}" />              
                <property name="generateDdl" value="${db.orm.generateDdl}" />               
                <property name="database" value="${db.type}"/>  
                <property name="databasePlatform" value="${db.orm.dialect}" />
            </bean>
        </property>  
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
            </props>
        </property>
    </bean>
    
   
</beans> 

We will use JAXB  and JPA annotation together in our POJO class.
All the annoted classes will be specified in persistence.xml and it should be in classpath

persistence.xml
 
 <?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="app_persist_unit" transaction-type="RESOURCE_LOCAL">
        <description>Oracle db Persistence Unit</description> 
         <class>com.opensourzesupport.rest.model.Product</class>
    </persistence-unit>
</persistence>


Web.xml(if using Jersey)

<?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">
  <display-name>spring-jpa-rest</display-name>
   <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
<!--    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>-->
     <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.opensourzesupport.rest</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app> 
 
Web.xml(if using RESTEasy )

<?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">
    <display-name>spring-jpa-rest</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    
    <!-- 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>/rest</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>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app> 

Product.java is our annoted POJO class.

Product.java
package com.opensourzesupport.rest.model;

import com.opensourzesupport.rest.util.Constants;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author Prasobh.K
 */
@XmlRootElement
@Entity
@NamedQueries({
    @NamedQuery(name = Constants.GET_PRODUCT,
    query = "select T from Product T")})
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {

    @Column
    @Id
    String name;
    @Column
    String description;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
 

ProductList.java
package com.opensourzesupport.rest.model;

import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author Prasobh.K
 */
@XmlRootElement(name="root")
public class ProductList {
  
   public  List product ;

    public List getProducts() {
        return product;
    }

    public void setProducts(List products) {
        this.product = products;
    }
}

ProductResource.java is our service class, where we will do all rest based annotations.

ProductResource.java
package com.opensourzesupport.rest.network;

import com.opensourzesupport.rest.model.Product;
import com.opensourzesupport.rest.model.ProductList;
import com.opensourzesupport.rest.util.Constants;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 *
 * @author Prasobh.K
 */
@Path("/service")
public class ProductResource {

    @PersistenceContext
    private EntityManager em = null;
    private PlatformTransactionManager platformTransactionManager = null;
    private List list = new ArrayList();

    public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
        this.platformTransactionManager = platformTransactionManager;
    }

    @GET
    @Produces({MediaType.APPLICATION_XML})
    @Path("/product/xml")
    public ProductList getXML() {
        ProductList productList = new ProductList();
        productList.setProducts(getRoot());
        return productList;
    }

    @GET
    @Produces({MediaType.TEXT_PLAIN})
    @Path("/product/save")
    public String saveProduct() {
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED));
        Product product = new Product();
        product.setName("TestProduct");
        product.setDescription("testing Persistance");
        String status = "Failed";
        try {
            em.persist(product);
            platformTransactionManager.commit(transactionStatus);
            status = "Success";
        } catch (Exception e) {
            System.out.println("Error" + e);
            platformTransactionManager.rollback(transactionStatus);
        }
        return status;
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/product/get")
    public Product getProduct() {
        try {
            Query query = em.createNamedQuery(Constants.GET_PRODUCT);
            Product p = (Product) query.getSingleResult();
            return p;
        } catch (Exception e) {
            System.out.println("Error" + e);
        }
        return null;
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/product/json")
    public List getJSON() {
        return getRoot();
    }

    @GET
    @Produces({MediaType.TEXT_HTML})
    @Path("/html")
    public List getHTML() {

        return getRoot();
    }

    public List getRoot() {
        List root = new ArrayList();
        Product product1 = new Product();
        product1.setName("IOS");
        product1.setDescription("Mobile");
        root.add(product1);
        Product product2 = new Product();
        product2.setName("Android");
        product2.setDescription("Mobile");
        root.add(product2);
        Product product3 = new Product();
        product3.setName("BlacBerry");
        product3.setDescription("Mobile");
        root.add(product3);
        Product product4 = new Product();
        product4.setName("J2ME");
        product4.setDescription("Mobile");
        root.add(product4);
        return root;
    }
}

 Output :

http://localhost:8080/webservice/rest/service/products/xml


<root>
    <product>
        <name>IOS</name>
        <description>Mobile</description>
    </product>
    <product>
        <name>Android</name>
        <description>Mobile</description>
    </product>
    <product>
        <name>BlacBerry</name>
        <description>Mobile</description>
    </product>
    <product>
        <name>J2ME</name>
        <description>Mobile</description>
    </product>
    <products>
        <name>IOS</name>
        <description>Mobile</description>
    </products>
    <products>
        <name>Android</name>
        <description>Mobile</description>
    </products>
    <products>
        <name>BlacBerry</name>
        <description>Mobile</description>
    </products>
    <products>
        <name>J2ME</name>
        <description>Mobile</description>
    </products>
</root>

http://localhost:8080/webservice/rest/service/products/json



{"product":
[{"name":"IOS","description":"Mobile"},
{"name":"Android","description":"Mobile"},
{"name":"BlacBerry","description":"Mobile"},
{"name":"J2ME","description":"Mobile"}]
}

http://localhost:8080/webservice/rest/service/product/save

Success

http://localhost:8080/webservice/rest/service/product/get


{"name":"TestProduct","description":"testing Persistance"}

if(RESTEasy )
include :
scannotation-1.0.3.jar
resteasy-jaxb-provider-2.3.4.Final.jar
resteasy-jaxrs-2.3.4.Final.jar
if(Jersey)
include :
jersey-core-1.14.jar
jersey-server-1.14.jar
jersey-spring-1.9.jar

Jar Files Used for Jersey















To Make RESTEasy to work with JBOSS(jboss-5.1.0.GA), please use the following configuration :

applicationContext.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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:flow="http://www.springframework.org/schema/webflow-config"
       xmlns:jms="http://www.springframework.org/schema/jms"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:osgi="http://www.springframework.org/schema/osgi"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
          http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd
          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
          http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd
          http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-3.0.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
          http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd ">
       
    <context:component-scan base-package="com.opensourzesupport.rest" />
<!--    <tx:annotation-driven   />-->
    <context:annotation-config/>
     <!-- specifying the property file   -->
    <bean id="placeholderPropertiesuserdao" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:application.properties" />
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
        <property name="ignoreUnresolvablePlaceholders" value="true" />
    </bean>
    
    <bean class="com.opensourzesupport.rest.network.ProductResource" p:platformTransactionManager-ref="transactionManager" />
  
    <!-- datasource information -->
    <bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close"  >
        <property name="uRL" value="${db.jdbc.url}" />
        <property name="user" value="${db.jdbc.user}" />
        <property name="password" value="${db.jdbc.password}" />
    </bean>
<!--    <bean class="com.opensourzesupport.rest.core.SpringApplicationContext"></bean>-->
   
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory"/>  
   
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >  
        <property name="persistenceUnitName" value="persistanceUnit"/>  
        <property name="dataSource" ref="dataSource"/>  
        <property name="persistenceXmlLocation" value="classpath:persistence.xml"/> 
        <property name="jpaVendorAdapter">  
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="${db.orm.showsql}" />              
                <property name="generateDdl" value="${db.orm.generateDdl}" />               
                <property name="database" value="${db.type}"/>  
                <property name="databasePlatform" value="${db.orm.dialect}" />
            </bean>
        </property>  
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
            </props>
        </property>
    </bean>
    
   
</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_2_5.xsd"
         id="WebApp_ID" version="2.5">
    <display-name>spring-jpa-rest</display-name>  
    <listener>
        <listener-class>
            org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
        </listener-class>
    </listener>
    
    <listener>
        <listener-class>
            org.jboss.resteasy.plugins.spring.SpringContextLoaderListener
        </listener-class>
    </listener>
    <servlet>
        <servlet-name>resteasy-servlet</servlet-name>
        <servlet-class>
            org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
        </servlet-class>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>rest</param-value>
    </context-param>
 
    <context-param>
        <param-name>resteasy.scan</param-name>
        <param-value>false</param-value>
    </context-param>
</web-app> 

ProductResource.java
package com.opensourzesupport.rest.network;

import com.opensourzesupport.rest.core.SpringApplicationContext;
import com.opensourzesupport.rest.model.Product;
import com.opensourzesupport.rest.model.ProductList;
import com.opensourzesupport.rest.util.Constants;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 *
 * @author Prasobh.K
 */
@Component
@Path("/service")
public class ProductResource {

    @PersistenceContext
    private EntityManager em = null;
    private PlatformTransactionManager platformTransactionManager = null;
    private List<ProductList> list = new ArrayList<ProductList>();

    public PlatformTransactionManager getPlatformTransactionManager() {
        if(platformTransactionManager == null){
            platformTransactionManager = (PlatformTransactionManager) SpringApplicationContext.getBean("transactionManager");
        }
        return platformTransactionManager;
    }


    
    public void setPlatformTransactionManager(PlatformTransactionManager platformTransactionManager) {
        System.out.println("setting ptm");
        this.platformTransactionManager = platformTransactionManager;
    }

    @GET
    @Produces({MediaType.APPLICATION_XML})
    @Path("/products/xml")
    public List<Product> getXML() {
      
         return getRoot();
    }

    @GET
    @Produces({MediaType.TEXT_PLAIN})
    @Path("/product/save")
    public String saveProduct() {
        System.out.println("platformTransactionManager "+platformTransactionManager);
        TransactionStatus transactionStatus =platformTransactionManager.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRED));
        Product product = new Product();
        product.setName("TestProduct");
        product.setDescription("testing Persistance");
        String status = "Failed";
        try {
            em.persist(product);
           platformTransactionManager.commit(transactionStatus);
            status = "Success";
        } catch (Exception e) {
            System.out.println("Error" + e);
           platformTransactionManager.rollback(transactionStatus);
        }
        return status;
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/product/get")
    public Product getProduct() {
        try {
            Query query = em.createNamedQuery(Constants.GET_PRODUCT);
            Product p = (Product) query.getSingleResult();
            return p;
        } catch (Exception e) {
            System.out.println("Error" + e);
        }
        return null;
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    @Path("/products/json")
    public List<Product> getJSON() {
        return getRoot();
    }

    @GET
    @Produces({MediaType.TEXT_HTML})
    @Path("/html")
    public List<Product> getHTML() {

        return getRoot();
    }

    public List<Product> getRoot() {
        List<Product> root = new ArrayList<Product>();
        Product product1 = new Product();
        product1.setName("IOS");
        product1.setDescription("Mobile");
        root.add(product1);
        Product product2 = new Product();
        product2.setName("Android");
        product2.setDescription("Mobile");
        root.add(product2);
        Product product3 = new Product();
        product3.setName("BlacBerry");
        product3.setDescription("Mobile");
        root.add(product3);
        Product product4 = new Product();
        product4.setName("J2ME");
        product4.setDescription("Mobile");
        root.add(product4);
        return root;
    }
}

Libraries Used :


No comments:

Post a Comment