In Get Start with HAL, I went through the basic steps to setup a Spring Boot project and get HAL resource to work. This article is for you to check out some relatively advanced features used in a real word project called STARS. The features to be introduced include typical structure of an HAL project, handling composite primary keys, applying security constraints and so on.
To be able to understand this article, you need to know the basis of HAL, Database, and Aspect Oriented Programming. You can find more information by following the link on each topic. In addition, since this article references a lot of code in project STARS, you may also need a runtime to download and run the project.
Example Project Synopsis
Sport Telemetry and Report System (STARS), a project adopting HAL, provides a web portal for users to store, manage, review and analyze the movement trajectory and body positioning data of their sport sessions. The version to be referencing in this article is v1.0.0-Alpha.
Structure of an HAL project
As a standard of RESTful Web Services, HAL inherits characteristics of REST, i.e. a clear separation between frontend and backend makes an HAL project to be standalone. It doesn’t care what type of clients to cooperate with.
As a Java implementation of HAL, Spring Framework features HAL through Spring Data REST. To better provide HAL standardized web API, Spring Data REST adopts aspect oriented programing (AOP) paradigm. Unlike traditional layered architecture, AOP allows developers to focus on special operations. Spring HAL automatically provides default CRUD operations. They are interceptable on different aspects to enable customized security constraints, content manipulations, associated operations, and so on.
Handling Composite Key
Composite Primary Key is a very common practice in database. To get it to work with HAL, there are 3 things you need to take care of: embedded primary key, JSON Representation, and conversion.
Embedded Primary Key
In JPA, composite primary key is also called embedded id, which is a special object containing all the columns of the primary key. In the example project, Record is a domain class representing the table stars_record, which has owner and title columns as a composite primary key. To represent this composite primary key, we need a special object called RecordPK as an embedded id.
Once you have the embedded id, the project still cannot work as expected because it doesn’t know how to represent id in the self-reflect URL since the id contains more than one part. To specify the format of the id, you need to override toString method in the embedded id object. Check toString() in RecordPK, you will find that each part of composite key is delimited by & sign.
So far, we have the way to represent composite primary key properly. The last thing is to convert the self-reflect URL back to the embedded id object. To do that, you need to implement a BackendIdConverter as a service. You should list all the domains that contain embedded id, and provide convertors for them. In addition, link toString method of embedded id objects to this service to make self-reflect URL to be properly displayed.
Apply Security Check
In Spring Data REST, the most common practice of securing the application would be just using Spring Security. This article is not about Spring Security, but the idea of how to apply it in an HAL project. So, if you are interested in how it is configured in the example project, you can still trace from WebSecurityConfig.java and read the comments.
To apply security on Record for example, you need to know what methods of the repository are provided by Spring. As we declared the interface RecordRepository, Spring will proxy this interface and its methods, and provides HAL REST APIs. You will find all the methods in RecordRepository.java.
From previous section, we talked about AOP. In RecordRepository, I used annotations as the way to adopt AOP to interfere the invocation of each method. @PreAuthorize is the only annotation I used for security check, although, there are many other annotations can be used in different scenario.
Instead of explaining every single annotation and its value, I would like to just pick a typical one:
@Override @PreAuthorize("hasRole('ADMIN')") Iterable<Record> findAll();
This method provides a web interface to return all the records in a JSON format when an HTTP GET request being sent to http://localhost:8080/rest/records. If there is no @PreAuthorize annotation, anyone can get all records in the system, which should not be the case. We want only admin users to be able to perform this query. So, by saying ” hasRole(‘ADMIN’)”, users without admin privilege will not be allowed to invoke this method. In this way, the project can be secured safely and efficiently.
In this article, we went through some advanced, but also commonly used features of Spring Data REST, which is a Java implementation of HAL. Since HAL and Spring Data REST are really big topics, I’m sure you will find more when doing the actual development. However, this article talks about the most important concepts you have to know, and I hope you enjoy working with HAL.