Home > Bruce's Posts, JPA, Spring Data > REST Service Using Spring Data

REST Service Using Spring Data

I was intrigued by Spring Data’s REST project which promises to, “… make it easy to expose JPA based repositories as RESTful endpoints.” To try it out I decided to create a service which provides famous quotes such as Albert Einstein’s.

Insanity: doing the same thing over and over again and expecting different results.

Consider a JPA entity that holds a quote.

@Entity
@Table(name = "quote")
public class Quote {
	@Id
	@GeneratedValue
	private Long id;
	private String quote;

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}

	public String getQuote() {
		return quote;
	}
	public void setQuote(String quote) {
		this.quote = quote;
	}

}

This is a very simple example of an entity. Just a id and text string.

Now create a repository service to expose CRUD style access to the entity. For now I’m just going to read a quote so all that’s needed is the following interface.

@RestResource(path = "quote")
public interface QuoteRepository extends PagingAndSortingRepository<Quote, Long> {
}

Spring Data provide a paging repository (PagingAndSortingRepository) and the simpler CrudRepository.

All the magic is in the @RestResource annotation. When the Spring REST Exporter starts up it will expose the QuoteRespository as a REST endpoint.

The Spring REST Exporter is a web application build on top of Spring MVC. The documentation is sparse but examples are pretty straight forward.

Also the Spring REST Exporter example uses gradle. I want to use a combination of Maven and Eclipse. It uses Java classes to configure the application environment. I wanted to use XML application context files. I also wanted multiple projects to encapsulate various applications and libraries. The entire source code is available here at github. Getting all the pieces just right took some time. I ran the quote war using Tomcat 7.

My quote endpoint will expose a list of quotes. In this case I’ve exposed paging and sorting possibilities.

http://localhost:8080/quote-json-server/quote produces a list of quotes stored in the repository.

{
  "links" : [ ],
  "content" : [ {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:8080/quote-json-server/quote/1"
    } ],
    "quote" : "Insanity: doing the same thing over and over again and expecting different results."
  }, {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:8080/quote-json-server/quote/2"
    } ],
    "quote" : "Better to remain silent and be thought a fool than to speak out and remove all doubt."
  } ],
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 1
  }
}

The output is a JSON serialization of the org.springframework.hateoas.PagedResources class. The serialization uses a HATEOAS (Hypermedia as the Engine of Application State) style format to provide a state representation of the resource.

In this case it is the state of the first page of the quotes list.

http://localhost:8080/quote-json-server/quote/1 provides the state of the first quote.

{
  "links" : [ {
    "rel" : "self",
    "href" : "http://localhost:8080/quote-json-server/quote/1"
  } ],
  "quote" : "Insanity: doing the same thing over and over again and expecting different results."
}

Having come from RPC as the method of choice REST takes a bit of getting used to. Roy T. Fielding’s REST APIs must be hypertext-driven discusses this. The biggest thing being individual resources are identified in requests. In this case a href (URL).

The next step is to create a client application to test the service. I created two clients. One a simple Java client and the other a simple Android application. REST clients are free to deserialize the response in any way they wish. I chose to use Spring’s RestTemplate API. Here’s the simple Java Client.

public class QuoteRestTemplateClient {
  private static final String URL = "http://localhost:8080/quote-json-server/quote/1";

  public static void main(String[] args) {
   RestTemplate restTemplate = new RestTemplate();

  HttpHeaders requestHeaders = new HttpHeaders();
  List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
  acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
  requestHeaders.setAccept(acceptableMediaTypes);
  HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
  restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());

  System.out.printf("url: %s\n", URL);
  try {
            ResponseEntity<Quote> entity = restTemplate.exchange(URL, HttpMethod.GET,              requestEntity, Quote.class);
            System.out.printf("json: %s\n", entity.getBody().toString());

            Quote quote = restTemplate.getForObject(URL, Quote.class);
            System.out.printf("quote: %s\n", quote.getQuote());
      } catch (RestClientException e) {
            e.printStackTrace();
    }
  }
}

The biggest challenge was creating the client side Quote class. There were actually a couple of challenges.

The Spring REST Explorer uses Spring’s HATEOAS API to wrap the JPA entity (entities in the case of a list). The response is either an instance of Resource or Resources. While I could have included the Spring’s HATEOAS jar with my application that would pulled in baggage that I wanted to avoid and as I found out later would also be problematic with the Android client. So I punted and just copied the six source files I needed into the project and edited them accordingly.

It would have been nice to have just used the service side Quote class with the client. This too was problematic because it’s annotated with JPA. This again would pull in jars I don’t want or need on the client side. Also, its getId() clashes with one found in the HATEOAS Identifier’s getId(). Here’s the client side Quote class.

public class Quote extends ResourceSupport {
	private String quote;

	public String getQuote() {
		return quote;
	}
	public void setQuote(String quote) {
		this.quote = quote;
	}
}

Pretty simple but then the example is simple. I’d truly want a way to easily generate the server and client side versions of Quote in a repeatable manner. Something to investigate on another day.

The Android application, if one discounts the necessary Android code, is identical to the Java client. It also needs the HATEOAS source files and the client side Quote class. Here’s the main Activity class.

public class MainActivity extends Activity {
    private static final String URL = "http://192.168.2.201:8080/quote-json-server/quote/1";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView t1 =(TextView)findViewById(R.id.textView1);
        t1.setText(URL);

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders requestHeaders = new HttpHeaders();
        List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
        acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
        requestHeaders.setAccept(acceptableMediaTypes);
        HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
        restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());

        String string;

        try {
        	ResponseEntity<Quote> entity = restTemplate.exchange(URL, HttpMethod.GET, requestEntity, Quote.class);
            string = entity.getBody().toString();
        } catch (RestClientException e) {
        	string = e.getMessage();
        }

        TextView t2 = (TextView)findViewById(R.id.textView2);
        t2.setText(string);

        Quote quote = restTemplate.getForObject(URL, Quote.class);
        TextView t3 =(TextView)findViewById(R.id.textView3);
        t3.setText(quote.getQuote());
    }
}

Here’s what it looks like.

 

Categories: Bruce's Posts, JPA, Spring Data
  1. harsh sondi
    January 15, 2013 at 5:01 am

    a lucid example to demystify RestExporter/Spring Data Rest without using gradle..

  1. No trackbacks yet.

Leave a comment