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).
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….)/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:C:Users{system.username}app
if you are on Windows /home/usr/{system.username}/app
if you are on Linux /Users/{system.username}/app
and
cloudstreetmarket-api:
<dependency> <groupId>org.springframework.hateoas</groupId> <artifactId>spring-hateoas</artifactId> <version>0.17.0.RELEASE</version> </dependency>
@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); } }
@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 + "]"; } }
@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); } }
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.
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:
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.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 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 |
---|---|
|
This creates a new |
Methods |
Description |
---|---|
|
This adds all the given links to the resource |
|
This adds all the given links to the resource |
|
This adds the given link to the resource |
|
This returns the link with |
|
This returns the link with the given |
|
This returns all the links contained in this resource |
|
This returns whether the resource contains a link with the given |
|
This returns whether the resource contains links at all |
|
This removes all the links added to the resource so far |
| |
| |
|
As introduced earlier, this class is all about links! We will see that Spring HATEOAS provides a small machinery around links.
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:
Methods |
Description |
---|---|
|
This returns the underlying entity |
|
This adds all the given links to the resource |
|
This adds all the given links to the resource |
|
This adds the given link to the resource |
|
This returns the link with a |
|
This returns the link with the given |
|
This returns all the links contained in this resource |
|
This returns whether the resource contains a link with the given |
|
This returns whether the resource contains links at all |
|
This removes all the links added to the resource so far |
| |
| |
|
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 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.
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.
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.
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: