RESTfu­­l Jav­a­ wit­h ­JAX­-­­RS 2.­0­ (Second Edition)

Spring and JAX-RS

This project is an example of using JAX-RS, Spring, and JPA all together in one application in a JAX-RS implementation portable way. You should check out the RESTEasy Spring integration for a simpler spring integration.

System Requirements:

  • Maven 3.0.4 or higher

Building the project:

  1. In root directory mvn clean install

This will build a WAR and run it with embedded Jetty.

Source Code

Hierarchy

ex14_2
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- restfully
    |   |           `-- shop
    |   |               |-- domain
    |   |               |   |-- Customer.java
    |   |               |   |-- Customers.java
    |   |               |   |-- LineItem.java
    |   |               |   |-- Order.java
    |   |               |   |-- Orders.java
    |   |               |   |-- Product.java
    |   |               |   `-- Products.java
    |   |               |-- persistence
    |   |               |   |-- CustomerEntity.java
    |   |               |   |-- LineItemEntity.java
    |   |               |   |-- OrderEntity.java
    |   |               |   `-- ProductEntity.java
    |   |               `-- services
    |   |                   |-- CustomerResource.java
    |   |                   |-- CustomerResourceBean.java
    |   |                   |-- EntityNotFoundExceptionMapper.java
    |   |                   |-- OrderResource.java
    |   |                   |-- OrderResourceBean.java
    |   |                   |-- ProductResource.java
    |   |                   |-- ProductResourceBean.java
    |   |                   |-- ShoppingApplication.java
    |   |                   |-- StoreResource.java
    |   |                   `-- StoreResourceBean.java
    |   |-- resources
    |   |   `-- META-INF
    |   |       |-- applicationContext.xml
    |   |       `-- persistence.xml
    |   `-- webapp
    |       `-- WEB-INF
    |           `-- web.xml
    `-- test
        `-- java
            `-- com
                `-- restfully
                    `-- shop
                        `-- test
                            `-- ShoppingTest.java

Details

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <parent>
        <groupId>com.oreilly.rest.workbook</groupId>
        <artifactId>jaxrs-2.0-workbook-pom</artifactId>
        <version>1.0</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.oreilly.rest.workbook</groupId>
    <artifactId>jaxrs-2.0-workbook-ex14_2</artifactId>
    <version>2.0</version>
    <packaging>war</packaging>
    <name>ex14_2</name>
    <description/>
    <repositories>
        <repository>
            <id>jboss</id>
            <name>jboss repo</name>
            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.jboss.spec.javax.servlet</groupId>
            <artifactId>jboss-servlet-api_3.0_spec</artifactId>
            <version>1.0.1.Final</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>3.0.12.Final-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>3.0.12.Final-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>async-http-servlet-3.0</artifactId>
            <version>3.0.12.Final-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>jaxrs-api</artifactId>
            <version>3.0.12.Final-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-servlet-initializer</artifactId>
            <version>3.0.12.Final-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxb-provider</artifactId>
            <version>3.0.12.Final-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>persistence-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.spec.javax.transaction</groupId>
            <artifactId>jboss-transaction-api_1.1_spec</artifactId>
            <version>1.0.1.Final</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.2.1</version>
        </dependency>
        <!-- In memory database. To replace MySQL for test -->
        <dependency>
            <groupId>hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <version>1.8.0.7</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.4.0.GA</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.5.8</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.5.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>2.8.1</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>ex14_2</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.0.6.v20130930</version>
                <configuration>
                    <webApp>
                        <contextPath>/</contextPath>
                    </webApp>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <stopKey>foo</stopKey>
                    <stopPort>9999</stopPort>
                    <stopWait>1</stopWait>
                </configuration>
                <executions>
                    <execution>
                        <id>start-jetty</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <scanIntervalSeconds>0</scanIntervalSeconds>
                            <daemon>true</daemon>
                        </configuration>
                    </execution>
                    <execution>
                        <id>stop-jetty</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>surefire-it</id>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                        <configuration>
                            <skip>false</skip>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
             version="1.0">

    <persistence-unit name="testdb" transaction-type="RESOURCE_LOCAL"/>
</persistence>

src/main/resources/META-INF/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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd"
       default-autowire="byName">

    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="jpaVendorAdapter">
            <bean
                    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false"/>
                <property name="generateDdl" value="true"/>
                <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/>
            </bean>
        </property>
    </bean>

    <bean id="dataSource"
          class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
        <property name="url" value="jdbc:hsqldb:test/db/myDB"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="transactionManager"
          class="org.springframework.orm.jpa.JpaTransactionManager"/>

    <tx:annotation-driven/>

    <bean
            class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

    <bean id="customer" class="com.restfully.shop.services.CustomerResourceBean"/>
    <bean id="product" class="com.restfully.shop.services.ProductResourceBean"/>
    <bean id="order" class="com.restfully.shop.services.OrderResourceBean"/>
    <bean id="store" class="com.restfully.shop.services.StoreResourceBean"/>


</beans>

src/main/webapp/WEB-INF/web.xml

<web-app>
    <context-param>
        <param-name>spring-beans-file</param-name>
        <param-value>META-INF/applicationContext.xml</param-value>
    </context-param>
</web-app>

src/main/java/com/restfully/shop/domain/Customer.java

package com.restfully.shop.domain;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "customer")
@XmlType(propOrder = {"firstName", "lastName", "street", "city", "state", "zip", "country"})
public class Customer
{
   private int id;
   private String firstName;
   private String lastName;
   private String street;
   private String city;
   private String state;
   private String zip;
   private String country;

   @XmlAttribute
   public int getId()
   {
      return id;
   }

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

   @XmlElement(name = "first-name")
   public String getFirstName()
   {
      return firstName;
   }

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

   @XmlElement(name = "last-name")
   public String getLastName()
   {
      return lastName;
   }

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

   @XmlElement
   public String getStreet()
   {
      return street;
   }

   public void setStreet(String street)
   {
      this.street = street;
   }

   @XmlElement
   public String getCity()
   {
      return city;
   }

   public void setCity(String city)
   {
      this.city = city;
   }

   @XmlElement
   public String getState()
   {
      return state;
   }

   public void setState(String state)
   {
      this.state = state;
   }

   @XmlElement
   public String getZip()
   {
      return zip;
   }

   public void setZip(String zip)
   {
      this.zip = zip;
   }

   @XmlElement
   public String getCountry()
   {
      return country;
   }

   public void setCountry(String country)
   {
      this.country = country;
   }

   @Override
   public String toString()
   {
      return "Customer{" +
              "id=" + id +
              ", firstName='" + firstName + '\'' +
              ", lastName='" + lastName + '\'' +
              ", street='" + street + '\'' +
              ", city='" + city + '\'' +
              ", state='" + state + '\'' +
              ", zip='" + zip + '\'' +
              ", country='" + country + '\'' +
              '}';
   }
}

src/main/java/com/restfully/shop/domain/Customers.java

package com.restfully.shop.domain;

import javax.ws.rs.core.Link;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@XmlRootElement(name = "customers")
public class Customers
{
   protected Collection<Customer> customers = new ArrayList<Customer>();
   protected List<Link> links;

   @XmlElementRef
   public Collection<Customer> getCustomers()
   {
      return customers;
   }

   public void setCustomers(Collection<Customer> customers)
   {
      this.customers = customers;
   }

   @XmlElement(name="link")
   @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
   public List<Link> getLinks()
   {
      return links;
   }

   public void setLinks(List<Link> links)
   {
      this.links = links;
   }

   @XmlTransient
   public URI getNext()
   {
      if (links == null) return null;
      for (Link link : links)
      {
         if ("next".equals(link.getRel())) return link.getUri();
      }
      return null;
   }

   @XmlTransient
   public URI getPrevious()
   {
      if (links == null) return null;
      for (Link link : links)
      {
         if ("previous".equals(link.getRel())) return link.getUri();
      }
      return null;
   }

}

src/main/java/com/restfully/shop/domain/LineItem.java

package com.restfully.shop.domain;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@XmlRootElement(name = "line-item")
public class LineItem
{
   protected int id;
   protected Product product;
   protected int quantity;

   @XmlAttribute
   public int getId()
   {
      return id;
   }

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

   @XmlElementRef
   public Product getProduct()
   {
      return product;
   }

   public void setProduct(Product product)
   {
      this.product = product;
   }

   public int getQuantity()
   {
      return quantity;
   }

   public void setQuantity(int quantity)
   {
      this.quantity = quantity;
   }

   @Override
   public String toString()
   {
      return "LineItem{" +
              "id=" + id +
              ", product=" + product +
              ", quantity=" + quantity +
              '}';
   }
}

src/main/java/com/restfully/shop/domain/Order.java

package com.restfully.shop.domain;

import javax.ws.rs.core.Link;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@XmlRootElement(name = "order")
@XmlType(propOrder = {"total", "date", "cancelled", "customer", "lineItems", "links"})
public class Order
{
   protected int id;
   protected boolean cancelled;
   protected List<LineItem> lineItems = new ArrayList<LineItem>();
   protected double total;
   protected String date;
   protected Customer customer;
   protected List<Link> links;

   @XmlAttribute
   public int getId()
   {
      return id;
   }

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

   public boolean isCancelled()
   {
      return cancelled;
   }

   public void setCancelled(boolean cancelled)
   {
      this.cancelled = cancelled;
   }

   @XmlElementWrapper(name = "line-items")
   public List<LineItem> getLineItems()
   {
      return lineItems;
   }

   public void setLineItems(List<LineItem> lineItems)
   {
      this.lineItems = lineItems;
   }

   public String getDate()
   {
      return date;
   }

   public void setDate(String date)
   {
      this.date = date;
   }

   public double getTotal()
   {
      return total;
   }

   public void setTotal(double total)
   {
      this.total = total;
   }

   @XmlElementRef
   public Customer getCustomer()
   {
      return customer;
   }

   public void setCustomer(Customer customer)
   {
      this.customer = customer;
   }

   @XmlElement(name="link")
   @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
   public List<Link> getLinks()
   {
      return links;
   }

   public void setLinks(List<Link> links)
   {
      this.links = links;
   }

   public void addLink(Link link)
   {
      if (links == null) links = new ArrayList<Link>();
      links.add(link);
   }

   @Override
   public String toString()
   {
      return "Order{" +
              "id=" + id +
              ", cancelled=" + cancelled +
              ", lineItems=" + lineItems +
              ", total=" + total +
              ", date='" + date + '\'' +
              ", customer=" + customer +
              '}';
   }
}

src/main/java/com/restfully/shop/domain/Orders.java

package com.restfully.shop.domain;

import javax.ws.rs.core.Link;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@XmlRootElement(name = "orders")
public class Orders
{
   protected Collection<Order> orders = new ArrayList<Order>();
   protected List<Link> links;

   @XmlElementRef
   public Collection<Order> getOrders()
   {
      return orders;
   }

   public void setOrders(Collection<Order> orders)
   {
      this.orders = orders;
   }

   @XmlElement(name="link")
   @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
   public List<Link> getLinks()
   {
      return links;
   }

   public void setLinks(List<Link> links)
   {
      this.links = links;
   }

   @XmlTransient
   public URI getNext()
   {
      if (links == null) return null;
      for (Link link : links)
      {
         if ("next".equals(link.getRel())) return link.getUri();
      }
      return null;
   }

   @XmlTransient
   public URI getPrevious()
   {
      if (links == null) return null;
      for (Link link : links)
      {
         if ("previous".equals(link.getRel())) return link.getUri();
      }
      return null;
   }

}

src/main/java/com/restfully/shop/domain/Product.java

package com.restfully.shop.domain;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@XmlRootElement(name = "product")
public class Product
{
   private int id;
   private String name;
   private double cost;

   @XmlAttribute
   public int getId()
   {
      return id;
   }

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

   public String getName()
   {
      return name;
   }

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

   public double getCost()
   {
      return cost;
   }

   public void setCost(double cost)
   {
      this.cost = cost;
   }
}

src/main/java/com/restfully/shop/domain/Products.java

package com.restfully.shop.domain;

import javax.ws.rs.core.Link;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@XmlRootElement(name = "products")
public class Products
{
   protected Collection<Product> products = new ArrayList<Product>();
   protected List<Link> links;

   @XmlElementRef
   public Collection<Product> getProducts()
   {
      return products;
   }

   public void setProducts(Collection<Product> products)
   {
      this.products = products;
   }

   @XmlElement(name="link")
   @XmlJavaTypeAdapter(Link.JaxbAdapter.class)
   public List<Link> getLinks()
   {
      return links;
   }

   public void setLinks(List<Link> links)
   {
      this.links = links;
   }

   @XmlTransient
   public URI getNext()
   {
      if (links == null) return null;
      for (Link link : links)
      {
         if ("next".equals(link.getRel())) return link.getUri();
      }
      return null;
   }

   @XmlTransient
   public URI getPrevious()
   {
      if (links == null) return null;
      for (Link link : links)
      {
         if ("previous".equals(link.getRel())) return link.getUri();
      }
      return null;
   }

}

src/main/java/com/restfully/shop/persistence/CustomerEntity.java

package com.restfully.shop.persistence;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity(name = "Customer")
public class CustomerEntity
{
   private int id;
   private String firstName;
   private String lastName;
   private String street;
   private String city;
   private String state;
   private String zip;
   private String country;

   @Id
   @GeneratedValue
   public int getId()
   {
      return id;
   }

   public void setId(int 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 getStreet()
   {
      return street;
   }

   public void setStreet(String street)
   {
      this.street = street;
   }

   public String getCity()
   {
      return city;
   }

   public void setCity(String city)
   {
      this.city = city;
   }

   public String getState()
   {
      return state;
   }

   public void setState(String state)
   {
      this.state = state;
   }

   public String getZip()
   {
      return zip;
   }

   public void setZip(String zip)
   {
      this.zip = zip;
   }

   public String getCountry()
   {
      return country;
   }

   public void setCountry(String country)
   {
      this.country = country;
   }

   @Override
   public String toString()
   {
      return "CustomerEntity {" +
              "id=" + id +
              ", firstName='" + firstName + '\'' +
              ", lastName='" + lastName + '\'' +
              ", street='" + street + '\'' +
              ", city='" + city + '\'' +
              ", state='" + state + '\'' +
              ", zip='" + zip + '\'' +
              ", country='" + country + '\'' +
              '}';
   }
}

src/main/java/com/restfully/shop/persistence/LineItemEntity.java

package com.restfully.shop.persistence;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Entity(name = "LineItem")
public class LineItemEntity
{
   protected int id;
   protected int quantity;
   protected ProductEntity product;

   @Id
   @GeneratedValue
   public int getId()
   {
      return id;
   }

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

   @ManyToOne
   public ProductEntity getProduct()
   {
      return product;
   }

   public void setProduct(ProductEntity product)
   {
      this.product = product;
   }

   public int getQuantity()
   {
      return quantity;
   }

   public void setQuantity(int quantity)
   {
      this.quantity = quantity;
   }
}

src/main/java/com/restfully/shop/persistence/OrderEntity.java

package com.restfully.shop.persistence;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Entity(name = "PurchaseOrder")
public class OrderEntity
{
   protected int id;
   protected boolean cancelled;
   protected List<LineItemEntity> lineItems = new ArrayList<LineItemEntity>();
   protected double total;
   protected String date;
   protected CustomerEntity customer;

   @Id
   @GeneratedValue
   public int getId()
   {
      return id;
   }

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

   public boolean isCancelled()
   {
      return cancelled;
   }

   public void setCancelled(boolean cancelled)
   {
      this.cancelled = cancelled;
   }

   @OneToMany(cascade = CascadeType.ALL)
   public List<LineItemEntity> getLineItems()
   {
      return lineItems;
   }

   public void setLineItems(List<LineItemEntity> lineItems)
   {
      this.lineItems = lineItems;
   }

   public String getDate()
   {
      return date;
   }

   public void setDate(String date)
   {
      this.date = date;
   }

   public double getTotal()
   {
      return total;
   }

   public void setTotal(double total)
   {
      this.total = total;
   }

   @ManyToOne
   public CustomerEntity getCustomer()
   {
      return customer;
   }

   public void setCustomer(CustomerEntity customer)
   {
      this.customer = customer;
   }
}

src/main/java/com/restfully/shop/persistence/ProductEntity.java

package com.restfully.shop.persistence;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Entity(name = "Product")
public class ProductEntity
{
   private int id;
   private String name;
   private double cost;

   @Id
   @GeneratedValue
   public int getId()
   {
      return id;
   }

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

   public String getName()
   {
      return name;
   }

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

   public double getCost()
   {
      return cost;
   }

   public void setCost(double cost)
   {
      this.cost = cost;
   }
}

src/main/java/com/restfully/shop/services/CustomerResource.java

package com.restfully.shop.services;

import com.restfully.shop.domain.Customer;
import com.restfully.shop.domain.Customers;
import org.jboss.resteasy.annotations.providers.jaxb.Formatted;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Path("/customers")
public interface CustomerResource
{
   @POST
   @Consumes("application/xml")
   Response createCustomer(Customer customer, @Context UriInfo uriInfo);

   @GET
   @Produces("application/xml")
   @Formatted
   Customers getCustomers(@QueryParam("start") int start,
                          @QueryParam("size") @DefaultValue("2") int size,
                          @QueryParam("firstName") String firstName,
                          @QueryParam("lastName") String lastName,
                          @Context UriInfo uriInfo);

   @GET
   @Path("{id}")
   @Produces({"application/xml", "application/json"})
   Customer getCustomer(@PathParam("id") int id);
}

src/main/java/com/restfully/shop/services/CustomerResourceBean.java

package com.restfully.shop.services;

import com.restfully.shop.domain.Customer;
import com.restfully.shop.domain.Customers;
import com.restfully.shop.persistence.CustomerEntity;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

@Transactional
public class CustomerResourceBean implements CustomerResource
{
   private EntityManager em;

   @PersistenceContext
   public void setEntityManager(EntityManager em)
   {
      this.em = em;
   }

   public Response createCustomer(Customer customer, UriInfo uriInfo)
   {
      CustomerEntity entity = new CustomerEntity();
      domain2entity(entity, customer);
      em.persist(entity);
      em.flush();

      System.out.println("Created customer " + entity.getId());
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.path(Integer.toString(entity.getId()));
      return Response.created(builder.build()).build();

   }

   public static void domain2entity(CustomerEntity entity, Customer customer)
   {
      entity.setId(customer.getId());
      entity.setFirstName(customer.getFirstName());
      entity.setLastName(customer.getLastName());
      entity.setStreet(customer.getStreet());
      entity.setCity(customer.getCity());
      entity.setState(customer.getState());
      entity.setZip(customer.getZip());
      entity.setCountry(customer.getCountry());
   }

   public static Customer entity2domain(CustomerEntity entity)
   {
      Customer cust = new Customer();
      cust.setId(entity.getId());
      cust.setFirstName(entity.getFirstName());
      cust.setLastName(entity.getLastName());
      cust.setStreet(entity.getStreet());
      cust.setCity(entity.getCity());
      cust.setState(entity.getState());
      cust.setZip(entity.getZip());
      cust.setCountry(entity.getCountry());
      return cust;
   }

   public Customers getCustomers(int start,
                                 int size,
                                 String firstName,
                                 String lastName,
                                 UriInfo uriInfo)
   {
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.queryParam("start", "{start}");
      builder.queryParam("size", "{size}");

      ArrayList<Customer> list = new ArrayList<Customer>();
      ArrayList<Link> links = new ArrayList<Link>();

      Query query = null;
      if (firstName != null && lastName != null)
      {
         query = em.createQuery("select c from Customer c where c.firstName=:first and c.lastName=:last");
         query.setParameter("first", firstName);
         query.setParameter("last", lastName);

      }
      else if (lastName != null)
      {
         query = em.createQuery("select c from Customer c where c.lastName=:last");
         query.setParameter("last", lastName);
      }
      else
      {
         query = em.createQuery("select c from Customer c");
      }

      List customerEntities = query.setFirstResult(start)
              .setMaxResults(size)
              .getResultList();

      for (Object obj : customerEntities)
      {
         CustomerEntity entity = (CustomerEntity) obj;
         list.add(entity2domain(entity));
      }
      // next link
      // If the size returned is equal then assume there is a next
      if (customerEntities.size() == size)
      {
         int next = start + size;
         URI nextUri = builder.clone().build(next, size);
         Link nextLink = Link.fromUri(nextUri).rel("next").type("application/xml").build();
         links.add(nextLink);
      }
      // previous link
      if (start > 0)
      {
         int previous = start - size;
         if (previous < 0) previous = 0;
         URI previousUri = builder.clone().build(previous, size);
         Link previousLink = Link.fromUri(previousUri).rel("previous").type("application/xml").build();
         links.add(previousLink);
      }
      Customers customers = new Customers();
      customers.setCustomers(list);
      customers.setLinks(links);
      return customers;
   }

   public Customer getCustomer(int id)
   {
      CustomerEntity customer = em.getReference(CustomerEntity.class, id);
      return entity2domain(customer);
   }

}

src/main/java/com/restfully/shop/services/EntityNotFoundExceptionMapper.java

package com.restfully.shop.services;

import javax.persistence.EntityNotFoundException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Provider
public class EntityNotFoundExceptionMapper implements ExceptionMapper<EntityNotFoundException>
{
   public Response toResponse(EntityNotFoundException exception)
   {
      System.out.println("*** EntityNotFoundExceptionMapper!!!!");
      return Response.status(Response.Status.NOT_FOUND).build();
   }
}

src/main/java/com/restfully/shop/services/OrderResource.java

package com.restfully.shop.services;

import com.restfully.shop.domain.Order;
import org.jboss.resteasy.annotations.providers.jaxb.Formatted;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Path("/orders")
public interface OrderResource
{
   @POST
   @Consumes("application/xml")
   Response createOrder(Order order, @Context UriInfo uriInfo);

   @POST
   @Path("purge")
   void purgeOrders();

   @HEAD
   @Produces("application/xml")
   Response getOrdersHeaders(@Context UriInfo uriInfo);

   @GET
   @Produces("application/xml")
   @Formatted
   Response getOrders(@QueryParam("start") int start,
                      @QueryParam("size") @DefaultValue("2") int size,
                      @Context UriInfo uriInfo);

   @POST
   @Path("{id}/cancel")
   void cancelOrder(@PathParam("id") int id);

   @GET
   @Path("{id}")
   @Produces("application/xml")
   Response getOrder(@PathParam("id") int id, @Context UriInfo uriInfo);

   @HEAD
   @Path("{id}")
   @Produces("application/xml")
   Response getOrderHeaders(@PathParam("id") int id, @Context UriInfo uriInfo);
}

src/main/java/com/restfully/shop/services/OrderResourceBean.java

package com.restfully.shop.services;

import com.restfully.shop.domain.Customer;
import com.restfully.shop.domain.LineItem;
import com.restfully.shop.domain.Order;
import com.restfully.shop.domain.Orders;
import com.restfully.shop.domain.Product;
import com.restfully.shop.persistence.CustomerEntity;
import com.restfully.shop.persistence.LineItemEntity;
import com.restfully.shop.persistence.OrderEntity;
import com.restfully.shop.persistence.ProductEntity;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

@Transactional
public class OrderResourceBean implements OrderResource
{
   private EntityManager em;

   @PersistenceContext
   public void setEntityManager(EntityManager em)
   {
      this.em = em;
   }

   protected static void domain2entity(OrderEntity entity, Order order)
   {
      entity.setId(order.getId());
      entity.setCancelled(order.isCancelled());
      entity.setDate(order.getDate());
      entity.setTotal(order.getTotal());
      CustomerEntity customerEntity = new CustomerEntity();
      CustomerResourceBean.domain2entity(customerEntity, order.getCustomer());
      entity.setCustomer(customerEntity);
      for (LineItem item : order.getLineItems())
      {
         LineItemEntity lineItem = new LineItemEntity();
         domain2entity(lineItem, item);
         entity.getLineItems().add(lineItem);
      }

   }

   public static void domain2entity(LineItemEntity entity, LineItem item)
   {
      entity.setId(item.getId());
      ProductEntity product = new ProductEntity();
      ProductResourceBean.domain2entity(product, item.getProduct());
      entity.setProduct(product);
      entity.setQuantity(item.getQuantity());
   }

   public static Order entity2domain(OrderEntity entity)
   {
      Order order = new Order();
      order.setId(entity.getId());
      order.setCancelled(entity.isCancelled());
      order.setDate(entity.getDate());
      order.setTotal(entity.getTotal());
      CustomerEntity customerEntity = entity.getCustomer();
      Customer customer = CustomerResourceBean.entity2domain(customerEntity);
      order.setCustomer(customer);
      for (LineItemEntity item : entity.getLineItems())
      {
         LineItem lineItem = entity2domain(item);
         order.getLineItems().add(lineItem);
      }
      return order;
   }

   public static LineItem entity2domain(LineItemEntity entity)
   {
      LineItem item = new LineItem();
      item.setId(entity.getId());
      Product product = ProductResourceBean.entity2domain(entity.getProduct());
      item.setProduct(product);
      item.setQuantity(entity.getQuantity());
      return item;
   }

   public static void addPurgeLinkHeader(UriInfo uriInfo, Response.ResponseBuilder builder)
   {
      UriBuilder absolute = uriInfo.getAbsolutePathBuilder();
      URI purgeUri = absolute.clone().path("purge").build();
      builder.link(purgeUri, "purge");
   }

   public Response createOrder(Order order, UriInfo uriInfo)
   {
      OrderEntity entity = new OrderEntity();
      domain2entity(entity, order);
      em.persist(entity);
      em.flush();
      System.out.println("Created order " + entity.getId());
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.path(Integer.toString(entity.getId()));
      return Response.created(builder.build()).build();

   }

   public void purgeOrders()
   {
      int updated = em.createQuery("delete from PurchaseOrder o where o.cancelled = true").executeUpdate();
   }

   public Response getOrdersHeaders(UriInfo uriInfo)
   {
      Response.ResponseBuilder builder = Response.ok();
      builder.type("application/xml");
      addPurgeLinkHeader(uriInfo, builder);
      return builder.build();
   }

   public Response getOrders(int start,
                             int size,
                             UriInfo uriInfo)
   {
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.queryParam("start", "{start}");
      builder.queryParam("size", "{size}");

      ArrayList<Order> list = new ArrayList<Order>();
      ArrayList<Link> links = new ArrayList<Link>();

      List orderEntities = em.createQuery("select p from PurchaseOrder p")
              .setFirstResult(start)
              .setMaxResults(size)
              .getResultList();

      for (Object obj : orderEntities)
      {
         OrderEntity entity = (OrderEntity) obj;
         Order order = entity2domain(entity);
         URI self = uriInfo.getAbsolutePathBuilder().path(Integer.toString(order.getId())).build();
         Link selfLink = Link.fromUri(self).rel("self").type("application/xml").build();
         order.addLink(selfLink);
         if (!order.isCancelled())
         {
            URI cancel = uriInfo.getAbsolutePathBuilder().path(Integer.toString(order.getId())).path("cancel").build();
            Link cancelLink = Link.fromUri(cancel).rel("cancel").type("application/xml").build();
            order.addLink(cancelLink);
         }
         list.add(order);
      }
      // next link
      // If the size returned is equal then assume there is a next
      if (orderEntities.size() == size)
      {
         int next = start + size;
         URI nextUri = builder.clone().build(next, size);
         Link nextLink = Link.fromUri(nextUri).rel("next").type("application/xml").build();
         links.add(nextLink);
      }
      // previous link
      if (start > 0)
      {
         int previous = start - size;
         if (previous < 0) previous = 0;
         URI previousUri = builder.clone().build(previous, size);
         Link previousLink = Link.fromUri(previousUri).rel("previous").type("application/xml").build();
         links.add(previousLink);
      }
      Orders orders = new Orders();
      orders.setOrders(list);
      orders.setLinks(links);
      Response.ResponseBuilder responseBuilder = Response.ok(orders);
      addPurgeLinkHeader(uriInfo, responseBuilder);
      return responseBuilder.build();
   }

   public static void addCancelHeader(UriInfo uriInfo, Response.ResponseBuilder builder)
   {
      UriBuilder absolute = uriInfo.getAbsolutePathBuilder();
      URI cancelUri = absolute.clone().path("cancel").build();
      builder.link(cancelUri, "cancel");
   }


   public void cancelOrder(int id)
   {
      OrderEntity order = em.getReference(OrderEntity.class, id);
      order.setCancelled(true);
   }


   public Response getOrder(int id, UriInfo uriInfo)
   {
      OrderEntity entity = em.getReference(OrderEntity.class, id);
      Order order = entity2domain(entity);
      URI self = uriInfo.getAbsolutePathBuilder().build();
      Link selfLink = Link.fromUri(self).rel("self").type("application/xml").build();
      order.addLink(selfLink);
      if (!order.isCancelled())
      {
         URI cancel = uriInfo.getAbsolutePathBuilder().path("cancel").build();
         Link cancelLink = Link.fromUri(cancel).rel("cancel").type("application/xml").build();
         order.addLink(cancelLink);
      }

      Response.ResponseBuilder builder = Response.ok(order);
      if (!order.isCancelled()) addCancelHeader(uriInfo, builder);
      return builder.build();
   }

   public Response getOrderHeaders(int id, UriInfo uriInfo)
   {
      OrderEntity order = em.getReference(OrderEntity.class, id);
      Response.ResponseBuilder builder = Response.ok();
      builder.type("application/xml");
      if (!order.isCancelled()) addCancelHeader(uriInfo, builder);
      return builder.build();
   }
}

src/main/java/com/restfully/shop/services/ProductResource.java

package com.restfully.shop.services;

import com.restfully.shop.domain.Product;
import com.restfully.shop.domain.Products;
import org.jboss.resteasy.annotations.providers.jaxb.Formatted;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Path("/products")
public interface ProductResource
{
   @POST
   @Consumes("application/xml")
   Response createProduct(Product customer, @Context UriInfo uriInfo);

   @GET
   @Produces("application/xml")
   @Formatted
   Products getProducts(@QueryParam("start") int start,
                        @QueryParam("size") @DefaultValue("2") int size,
                        @QueryParam("name") String name,
                        @Context UriInfo uriInfo);

   @GET
   @Path("{id}")
   @Produces("application/xml")
   Product getProduct(@PathParam("id") int id);
}

src/main/java/com/restfully/shop/services/ProductResourceBean.java

package com.restfully.shop.services;

import com.restfully.shop.domain.Product;
import com.restfully.shop.domain.Products;
import com.restfully.shop.persistence.ProductEntity;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

@Transactional
public class ProductResourceBean implements ProductResource
{
   private EntityManager em;

   @PersistenceContext
   public void setEntityManager(EntityManager em)
   {
      this.em = em;
   }

   public Response createProduct(Product product, UriInfo uriInfo)
   {
      ProductEntity entity = new ProductEntity();
      domain2entity(entity, product);
      em.persist(entity);
      em.flush();

      System.out.println("Created product " + entity.getId());
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.path(Integer.toString(entity.getId()));
      return Response.created(builder.build()).build();

   }

   public static void domain2entity(ProductEntity entity, Product product)
   {
      entity.setId(product.getId());
      entity.setCost(product.getCost());
      entity.setName(product.getName());
   }

   public static Product entity2domain(ProductEntity entity)
   {
      Product product = new Product();
      product.setId(entity.getId());
      product.setCost(entity.getCost());
      product.setName(entity.getName());
      return product;
   }

   public Products getProducts(int start,
                               int size,
                               String name,
                               UriInfo uriInfo)
   {
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.queryParam("start", "{start}");
      builder.queryParam("size", "{size}");

      ArrayList<Product> list = new ArrayList<Product>();
      ArrayList<Link> links = new ArrayList<Link>();

      Query query = null;
      if (name != null)
      {
         query = em.createQuery("select p from Product p where p.name=:name");
         query.setParameter("name", name);

      }
      else
      {
         query = em.createQuery("select p from Product p");
      }


      List productEntities = query.setFirstResult(start)
              .setMaxResults(size)
              .getResultList();

      for (Object obj : productEntities)
      {
         ProductEntity entity = (ProductEntity) obj;
         list.add(entity2domain(entity));
      }
      // next link
      // If the size returned is equal then assume there is a next
      if (productEntities.size() == size)
      {
         int next = start + size;
         URI nextUri = builder.clone().build(next, size);
         Link nextLink = Link.fromUri(nextUri).rel("next").type("application/xml").build();
         links.add(nextLink);
      }
      // previous link
      if (start > 0)
      {
         int previous = start - size;
         if (previous < 0) previous = 0;
         URI previousUri = builder.clone().build(previous, size);
         Link previousLink = Link.fromUri(previousUri).rel("previous").type("application/xml").build();
         links.add(previousLink);
      }
      Products products = new Products();
      products.setProducts(list);
      products.setLinks(links);
      return products;
   }

   public Product getProduct(int id)
   {
      ProductEntity product = em.getReference(ProductEntity.class, id);
      return entity2domain(product);
   }

}

src/main/java/com/restfully/shop/services/StoreResource.java

package com.restfully.shop.services;

import javax.ws.rs.HEAD;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Path("/shop")
public interface StoreResource
{
   @HEAD
   Response head(@Context UriInfo uriInfo);
}

src/main/java/com/restfully/shop/services/StoreResourceBean.java

package com.restfully.shop.services;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class StoreResourceBean implements StoreResource
{
   public Response head(UriInfo uriInfo)
   {
      UriBuilder absolute = uriInfo.getBaseUriBuilder();
      URI customerUrl = absolute.clone().path("customers").build();
      URI orderUrl = absolute.clone().path("orders").build();
      URI productUrl = absolute.clone().path("products").build();
      javax.ws.rs.core.Link customers = javax.ws.rs.core.Link.fromUri(customerUrl).rel("customers").type("application/xml").build();
      javax.ws.rs.core.Link orders = javax.ws.rs.core.Link.fromUri(orderUrl).rel("orders").type("application/xml").build();
      javax.ws.rs.core.Link products = javax.ws.rs.core.Link.fromUri(productUrl).rel("products").type("application/xml").build();

      Response.ResponseBuilder builder = Response.ok().links(customers, orders, products);
      return builder.build();
   }
}

src/main/java/com/restfully/shop/services/ShoppingApplication.java

package com.restfully.shop.services;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import java.util.HashSet;
import java.util.Set;

@ApplicationPath("/services")
public class ShoppingApplication extends Application
{
   private Set<Class<?>> classes = new HashSet<Class<?>>();

   public ShoppingApplication()
   {
      classes.add(EntityNotFoundExceptionMapper.class);
   }

   public Set<Class<?>> getClasses()
   {
      return classes;
   }

   protected ApplicationContext springContext;

   @Context
   protected ServletContext servletContext;

   public Set<Object> getSingletons()
   {
      try
      {
         InitialContext ctx = new InitialContext();
         String xmlFile = (String)servletContext.getInitParameter("spring-beans-file");
         springContext = new ClassPathXmlApplicationContext(xmlFile);
      }
      catch (Exception ex)
      {
         ex.printStackTrace();
         throw new RuntimeException(ex);
      }
      HashSet<Object> set = new HashSet();
      set.add(springContext.getBean("customer"));
      set.add(springContext.getBean("order"));
      set.add(springContext.getBean("product"));
      set.add(springContext.getBean("store"));
      return set;
   }

}

src/test/java/com/restfully/shop/test/ShoppingTest.java

package com.restfully.shop.test;

import com.restfully.shop.domain.Customer;
import com.restfully.shop.domain.Customers;
import com.restfully.shop.domain.LineItem;
import com.restfully.shop.domain.Order;
import com.restfully.shop.domain.Product;
import com.restfully.shop.domain.Products;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.Date;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public class ShoppingTest
{
   private static Client client;

   @BeforeClass
   public static void initClient()
   {
      client = ClientBuilder.newClient();
   }

   @AfterClass
   public static void closeClient()
   {
      client.close();
   }

   public void populateDB() throws Exception
   {
      Response response = client.target("http://localhost:8080/services/shop").request().head();
      javax.ws.rs.core.Link products = response.getLink("products");
      response.close();

      System.out.println("** Populate Products");

      Product product = new Product();
      product.setName("iPhone");
      product.setCost(199.99);
      response = client.target(products).request().post(Entity.xml(product));
      Assert.assertEquals(201, response.getStatus());
      response.close();

      product = new Product();
      product.setName("MacBook Pro");
      product.setCost(3299.99);
      response = client.target(products).request().post(Entity.xml(product));
      Assert.assertEquals(201, response.getStatus());
      response.close();

      product = new Product();
      product.setName("iPod");
      product.setCost(49.99);
      response = client.target(products).request().post(Entity.xml(product));
      Assert.assertEquals(201, response.getStatus());
      response.close();
   }

   @Test
   public void testCreateOrder() throws Exception
   {
      populateDB();

      Response response = client.target("http://localhost:8080/services/shop").request().head();
      javax.ws.rs.core.Link customers = response.getLink("customers");
      javax.ws.rs.core.Link products = response.getLink("products");
      javax.ws.rs.core.Link orders = response.getLink("orders");
      response.close();

      System.out.println("** Buy an iPhone for Bill Burke");
      System.out.println();
      System.out.println("** First see if Bill Burke exists as a customer");
      Customers custs = client.target(customers)
              .queryParam("firstName", "Bill")
              .queryParam("lastName", "Burke")
              .request().get(Customers.class);
      Customer customer = null;
      if (custs.getCustomers().size() > 0)
      {
         System.out.println("- Found a Bill Burke in the database, using that");
         customer = custs.getCustomers().iterator().next();
      }
      else
      {
         System.out.println("- Cound not find a Bill Burke in the database, creating one.");
         customer = new Customer();
         customer.setFirstName("Bill");
         customer.setLastName("Burke");
         customer.setStreet("222 Dartmouth Street");
         customer.setCity("Boston");
         customer.setState("MA");
         customer.setZip("02115");
         customer.setCountry("USA");
         response = client.target(customers).request().post(Entity.xml(customer));
         Assert.assertEquals(201, response.getStatus());
         URI uri = response.getLocation();
         response.close();

         customer = client.target(uri).request().get(Customer.class);
      }

      System.out.println();
      System.out.println("Search for iPhone in the Product database");
      Products prods = client.target(products)
              .queryParam("name", "iPhone")
              .request()
              .get(Products.class);
      Product product = null;
      if (prods.getProducts().size() > 0)
      {
         System.out.println("- Found iPhone in the database.");
         product = prods.getProducts().iterator().next();
      }
      else
      {
         throw new RuntimeException("Failed to find an iPhone in the database!");
      }

      System.out.println();
      System.out.println("** Create Order for iPhone");
      LineItem item = new LineItem();
      item.setProduct(product);
      item.setQuantity(1);
      Order order = new Order();
      order.setTotal(product.getCost());
      order.setCustomer(customer);
      order.setDate(new Date().toString());
      order.getLineItems().add(item);
      response = client.target(orders).request().post(Entity.xml(order));
      Assert.assertEquals(201, response.getStatus());
      response.close();

      System.out.println();
      System.out.println("** Show all orders.");
      String xml = client.target(orders).request().get(String.class);
      System.out.println(xml);
   }
}