Turning DTOs into Spring HATEOAS resources

This recipe presents how to create Spring HATEOAS resources. Even if the emphasis here is on one specific resource—IndexResource (in place of the former IndexOverviewDTO), feel free to browse cloudstreetmarket-api and cloudstreetmarket-core to discover more changes.

The HATEOAS principle has been applied at this stage to all the resources that make the core of our business, which strongly reflects the financial data structure of Yahoo! (indices, quotes, products, historical data, graphs, and so on).

How to do it…

  1. From the Git Perspective in Eclipse, checkout the latest version of the v6.x.x branch. Then, run a maven clean install command on the cloudstreetmarket-parent module (right-click on the Maven Clean menu under Run as… and then again on Maven Install under Run as…) followed by a click on Maven Update Project menu to synchronize Eclipse with the Maven configuration (right-click on the module and then navigate to Maven | Update Project….)

    Note

    This branch includes SQL scripts that prepopulate the database with real financial data coming from Yahoo!.

  2. Among the pulled changes, a new /app configuration directory shows up at the same level as cloudstreetmarket-parent and zipcloud-parent. This /app directory has to be copied to your system's home directory:
    • Copy it to C:Users{system.username}app if you are on Windows
    • Copy it to /home/usr/{system.username}/app if you are on Linux
    • If you are on Mac OS X, copy it at /Users/{system.username}/app
  3. Spring HATEOAS comes with the following dependency. This dependency has been added to cloudstreetmarket-parent, cloudstreetmarket-core, and cloudstreetmarket-api:
    <dependency>
      <groupId>org.springframework.hateoas</groupId>
      <artifactId>spring-hateoas</artifactId>
      <version>0.17.0.RELEASE</version>
    </dependency>
  4. As the recipe title suggests, the goal is to get rid of the existing DTOs that were exposed with the REST API. We have, for now, removed IndexOverviewDTO, MarketOverviewDTO, ProductOverviewDTO, and StockProductOverviewDTO.
  5. Those DTOs have been replaced by these classes: IndexResource, StockProductResource, ChartResource, ExchangeResource, IndustryResource, and MarketResource.
  6. As shown with IndexResource, which is presented as follows, all these new classes inherit the Spring HATEOAS Resource class:
    @XStreamAlias("resource")
    public class IndexResource extends Resource<Index> {
      public static final String INDEX = "index";
      public static final String INDICES = "indices";
      public static final String INDICES_PATH = "/indices";
    
      public IndexResource(Index content, Link... links) {
        super(content, links);
      }
    }
  7. As you can see, with IndexResource, resources are created from JPA entities (here, Index.java). These entities are stored in the Resource supertype under the content property name.
  8. We have transformed the JPA entities, abstracting their @Id in an implementation of the Identifiable interface:
    @Entity
    @Table(name="index_value")
    @XStreamAlias("index")
    public class Index extends ProvidedId<String> {
    
      private String name;
    
      @Column(name="daily_latest_value")
      private BigDecimal dailyLatestValue;
    
      @Column(name="daily_latest_change")
      private BigDecimal dailyLatestChange;
    
      @Column(name="daily_latest_change_pc")
      private BigDecimal dailyLatestChangePercent;
    
      @Column(name = "previous_close")
      private BigDecimal previousClose;
    
      private BigDecimal open;
    
      private BigDecimal high;
    
      private BigDecimal low;
    
      @ManyToOne(fetch = FetchType.EAGER)
      @JsonSerialize(using=IdentifiableSerializer.class)
      @JsonProperty("exchangeId")
      @XStreamConverter(value=IdentifiableToIdConverter.class, strings={"id"})
      @XStreamAlias("exchangeId")
       private Exchange exchange;
    
      @JsonIgnore
      @XStreamOmitField
      @ManyToMany(fetch = FetchType.LAZY)
      @JoinTable(name = "stock_indices", joinColumns = 
      {@JoinColumn(name = "index_code") },
      inverseJoinColumns = {@JoinColumn(name = "stock_code")})
      private Set<StockProduct> components = new LinkedHashSet<>();
    
      @Column(name="last_update", insertable=false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    
      @Temporal(TemporalType.TIMESTAMP)
      private Date lastUpdate;
    
      public Index(){}
      
      public Index(String indexId) {
        setId(indexId);
      }
      
      //getters & setters
    
        @Override
        public String toString() {
        return "Index [name=" + name + ", dailyLatestValue=" + dailyLatestValue + ", dailyLatestChange=" + dailyLatestChange + ", dailyLatestChangePercent=" + dailyLatestChangePercent + ", previousClose=" + previousClose + ", open=" + open + ", high=" + high + ", low=" + low + ", exchange=" + exchange + ", lastUpdate=" + lastUpdate + ", id=" + id + "]";
        }
      }
  9. Here are the details of the ProvidedId class, which is one of our Identifiable implementations:
    @MappedSuperclass
    public class ProvidedId<ID extends Serializable> implements Identifiable<ID> {
      @Id 
      protected ID id;
      @Override
      public ID getId() {
        return id;
      }
      public void setId(ID id) {
        this.id = id;
      }
      @Override
      public String toString() {
        return id;
      }
      @Override
      public int hashCode() {
        return Objects.hash(id);
      }
      @Override
      public boolean equals(Object obj) {
        if (this == obj)
          return true;
        if (obj == null)
          return false;
        if (getClass() != obj.getClass())
          return false;
        ProvidedId <?> other = (ProvidedId <?>) obj;
        return Objects.equals(this.id, other.id);
      }
    }

How it works...

One new Spring dependency, a few new resource objects (Resource subclasses), and finally some modifications to our Entities so that they implement the Identifiable interface. Let's debrief all this in detail.

Spring HATEOAS resources

As introduced at the beginning of this chapter, HATEOAS is about links. It is fair to say that we can expect, as part of the Framework, an existing Type that supports and standardizes the representation of links.

This is the role of the ResourceSupport class (part of Spring HATEOAS): to support the collection and management of links attached to a resource.

Alternatively, a REST resource is also a content. The Framework also offers a Resource class that already inherits ResourceSupport.

To summarize, using Spring HATEOAS, we can decide to model our resource objects (IndexResource, StockProductResource, and so on) in two different ways:

  • We can model them either by making them inherit ResourceSupport directly. By doing so, we have to manage the resource-content as part of the wrapper object by ourselves. The content here is out of control for the Framework.
  • We can also model them by making them inherit the generic Resource<T> class whose Type T corresponds to the Type of the POJO content for the resource. This is the strategy we have chosen. The Framework offers goodies for our resource-object (Inde3xResource) on content binding, link creation, and even at the controller's level. We will see all this soon.

The ResourceSupport class

The ResourceSupport class is an object that implements Identifiable<Link>:

public class ResourceSupport extends Object implements Identifiable<Link>

The following is a sample from the ResourceSupport JavaDoc, which will provide you with an insight on its constructors and methods:

Constructors

Description

ResourceSupport()

This creates a new ResourceSupport class

Methods

Description

Void add(Iterable<Link> links)

This adds all the given links to the resource

Void add(Link... links)

This adds all the given links to the resource

Void add(Link link)

This adds the given link to the resource

Link getId()

This returns the link with rel of Link.REL_SELF

Link getLink(String rel)

This returns the link with the given rel

List<Link> getLinks()

This returns all the links contained in this resource

boolean hasLink(String rel)

This returns whether the resource contains a link with the given rel

boolean hasLinks()

This returns whether the resource contains links at all

boolean removeLinks()

This removes all the links added to the resource so far

Boolean equals(Object obj)

 

int hashCode()

 

String toString()

 

As introduced earlier, this class is all about links! We will see that Spring HATEOAS provides a small machinery around links.

The Resource class

The Resource class is a wrapper for a POJO. The POJO is stored in a content property of this class. A Resource class natively extends ResourceSupport:

public class Resource<T> extends ResourceSupport

Here is a sample from the Resource JavaDoc that provides an insight into its constructors and methods:

Constructors

Description

Resource(T content, Iterable<Link> links)

This creates a new resource with the given content and links

Resource(T content, Link... links)

This creates a new resource with the given content and links (optional)

Methods

Description

TgetContent()

This returns the underlying entity

void add(Iterable<Link> links)

This adds all the given links to the resource

void add(Link... links)

This adds all the given links to the resource

void add(Link link)

This adds the given link to the resource

Link getId()

This returns the link with a rel of Link.REL_SELF

Link getLink(String rel)

This returns the link with the given rel

List<Link> getLinks()

This returns all the links contained in this resource

boolean hasLink(String rel)

This returns whether the resource contains a link with the given rel

boolean hasLinks()

This returns whether the resource contains links at all

boolean removeLinks()

This removes all the links added to the resource so far

Boolean equals(Object obj)

 

int hashCode()

 

String toString()

 

Two handy constructors, one getter for the content, and all the link-related helpers, this is what the Resource class is made of.

The Identifiable interface

The Identifiable interface plays a central role in Spring HATEOAS, since the key classes Resource, ResourceSupport, Resources, and PagedResources classes, which we'll present later on, are all Identifiable implementations. We will present later on, all these key classes.

The Identifiable interface is a Spring HATEOAS one-method interface (a generic interface) that is used to define an Id in an object:

public interface Identifiable<ID extends Serializable> {
  ID getId();
}

Consequently, the Framework uses this method to retrieve the ID with very few requirements about the nature of the passed-in object. With the capability of a class to implement several interfaces, it is costless to add such a qualifier to an object. Also, the contract of this interface is minimal.

The most important use of this interface (and method) by the framework is to build links out of a Resource object. Have a look at the slash method of LinkBuilderSupport. You will note that, if ID is not an instance of Identifiable (this is what it usually ends up with), the Link is appended with the toString() representation of the ID type.

Tip

Bear this behavior in mind if you are thinking of implementing custom ID types.

Abstracting the Entities' @Id

If you plan to stick with Spring HATEOAS without extending it to Spring Data REST, it is probably not an absolute necessity to decouple the base entities from their @Id. At least not in the way we did it.

Here, this practice comes from Oliver Gierke, in his Spring RestBucks application. Spring RestBucks is a showcase application for several modern Spring REST features.

Note

Oliver Gierke is the Spring Data lead developer at Pivotal Software, Inc.. He has also been involved in Spring HATEOAS. Spring Data is an amazing project and product. We can trust Oliver Gierke for his vision and decisions.

In his AsbtractId implementation, O. Gierke defines the Id property as private and annotates it as @JsonIgnore. He drives us toward the nonexposure of the Id attribute as part of the resource-content. In REST, the ID of a resource should be its URI.

If you have the chance to take a look at Spring Data REST, this approach fully makes sense as part of the Framework, which strongly correlates REST resources to Spring Data repositories.

We have made the choice of not covering Spring Data REST as part of this book. However, not exposing entity IDs is not critical for our application. For these reasons, and also because we wish to maintain conventionality on this point in regard to the Chapter 7, Developing CRUD Operations and Validati ons, IDs will be exposed as resource-attributes.

There's more…

If our HATEOAS introduction wasn't clear enough to give you an idea of the principle, do read this presentation from Pivotal (Spring.io) at:

https://spring.io/understanding/HATEOAS

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset