Permalink
3675aef Aug 15, 2013
2030 lines (1549 sloc) 103 KB

The Spring REST Stack

This tutorial introduces Spring’s comprehensive support for REST-powered web applications.

Today’s applications don’t exist in a vacuum. They are distributed, portable, mobile, integrated, social, and connected. Devices are small enough now that there’s no reason an application can’t go with you. We’ve seen the transformation that the rise of smart phones and tablets has had on our lives. What’s powering this transformation? How are these systems communicating? They’re certainly not using SOAP. Or CORBA. Most of them are using REST.

The Richardson Maturity Model

REST is a constraint on HTTP that was originally proffered by Dr. Roy Fielding in his 2000 doctoral dissertation. It is more a style than a standard. REST is not an all-or-nothing technology choice. Users can, and often do, choose to embrace the parts of REST that most effectively suit their use cases. APIs today are often said to be “RESTful” — or not — according to the depth of their compliance with the principals of REST.

We can use the Richardson Maturity Model by Leaonard Richardson to classify REST APIs. I think Martin Fowler does a good job explaining the concepts of the various levels, so we won’t endeavor to duplicate their explanations here. Here’s an abbreviated look.

  • Level 0 (The Swamp of POX) - In this level, the use of HTTP is almost incidental. HTTP is being used merely as the transport. For example, while SOAP services are often deployed over HTTP, they don’t have to be. They could, just as readily, be deployed over JMS or SMTP. Such services are said to be the least RESTful. POX refers to plain old XML, as many technologies in this level – including SOAP and XML-RPC - use HTTP as a transport for data encoded in XML, usually in a schema that models remote procedure call semantics.

  • Level 1 (Resources) - In this level, a singular service endpoint expands into multiple, distinct HTTP resources, corresponding to objects in your system, like a customer record, or a user record. Responses from these resources might reference other resources by their URIs in this topology. Here, for example, you might expose resources for each entity in a system, like /users/1, /users/2, etc.

  • Level 2 (Transport Native Properties) - In this level the API leverages transport-specific capabilities like headers, status codes, and verbs to build idiomatic HTTP APIs. This is an acceptable entry point into REST architectures.

  • Level 3 (Hypermedia) - The final level introduces something that you often hear referred to under the ugly acronym of HATEOAS (Hypertext As The Engine Of Application State). It involves decoupling the API from the URI topology and exposing navigation options to the client as response payload meta-data. This navigation meta-data is conveyed using a wrapper structure for response payloads that contain a collection of navigation options (link), and the payload intended for conveyance.

It’s interesting that most REST framework technologies will start you off at Level 2. If you’re using just Spring MVC and want to build RESTful services, this is where you start, by default. Let’s look at how to build such an API with Spring MVC. Before we can do that, however, we need to understand the domain of the backend service.

Our Customer Relationship Manager

Let’s look at an example that has a simple domain: User entities that contain collections of Customer records. Our service tier has the contract shown in Listing JL-1.

Our CrmService supports user profile and customer record manipulation
import org.springframework.http.MediaType;

import java.util.Collection;

public interface CrmService {

	ProfilePhoto readUserProfilePhoto(long userId);

	void writeUserProfilePhoto(long userId, MediaType mediaType, byte[] bytesForProfilePhoto);

	User findById(long userId);

	User createUser(String username, String password, String firstName, String lastName);

	User removeUser(long userId);

	User updateUser(long userId, String username, String password, String firstName, String lastName);

	User findUserByUsername(String username);

	Customer removeAccount(long userId, long customerId);

	Customer addAccount(long userId, String firstName, String lastName);

	Collection<Customer> loadCustomerAccounts(long userId);

	Customer findCustomerById(long customerId);

	// simple abstraction to hold
	// information about the profile photo.
	static class ProfilePhoto {

		private Long userId;
		private byte[] photo;
		private MediaType mediaType;
		public ProfilePhoto(long userId, byte[] data, MediaType mediaType) {
			this.mediaType = mediaType;
			this.photo = data;
			this.userId = userId;
		}
		public MediaType getMediaType() {
			return this.mediaType;
		}
		public byte[] getPhoto() {
			return this.photo;
		}
		public Long getUserId() {
			return this.userId;
		}
	}
}

It in turn delegates to a repository type that we have defined as shown in Listing JL-2.

The interface to our Spring Data JPA-powered repository for working with the JPA entity User.
package com.jl.crm.services;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.repository.annotation.RestResource;

import java.util.List;

public interface UserRepository extends PagingAndSortingRepository<User, Long> {

	User findByUsername(@Param ("username") String username);

	List<User> findUsersByFirstNameOrLastNameOrUsername(
		@Param ("firstName") String firstName,
		@Param ("lastName") String lastName,
		@Param ("username") String username);
}

The interface extends Spring Data’s PagingAndSortingRepository, which provides generic methods for working with JPA entities. In our case, we’re typing this to User entities, which have a generic key of type Long.

The more interesting thing here are the two methods defined in the interface, findByUsername and findUsersByFirstNameOrLastNameOrUsername. These methods will be implemented dynamically by Spring Data. Roughly, the dynamic JPA implementation of findUsersByFirstNameOrLastNameOrUsername will use the JPA EntityManager to run a JPA query of roughly the form SELECT U FROM User U WHERE U.firstName = ? OR U.lastName = ? OR U.username = ?. Pretty neat, right?

The Web Application

The web application hosting our REST API is a standard Servlet 3-compatible Spring MVC application. The application uses the Servlet 3 facility to bootstrap the container programmatically (as opposed to using a web.xml)

The CrmWebApplicationInitializer (shown in Listing JL-3) sets up the standard two-tiered configuration in Spring MVC applications: web-application-global Spring contexts are initialized as parents to DispatcherServlet-local contexts. Template methods make it easy to register Servlet infrastructure pieces like Filter instances, and to customize how the servlet was registered.

The class CrmWebApplicationInitializer configures or Servlet 3 context and our Spring application contexts.
package com.jl.crm.web;

import com.jl.crm.services.ServiceConfiguration;
import org.springframework.context.annotation.*;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.*;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.servlet.*;
import java.io.File;

public class CrmWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	private int maxUploadSizeInMb = 5 * 1024 * 1024; // 5 MB

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class<?>[]{ServiceConfiguration.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class<?>[]{WebMvcConfiguration.class};
	}

	@Override
	protected String[] getServletMappings() {
		return new String[]{"/"};
	}

	@Override
	protected Filter[] getServletFilters() {
		return new Filter[]{new HiddenHttpMethodFilter(), new MultipartFilter()};
	}

	@Override
	protected void customizeRegistration(ServletRegistration.Dynamic registration) {
		File uploadDirectory = ServiceConfiguration.CRM_STORAGE_UPLOADS_DIRECTORY;
		MultipartConfigElement multipartConfigElement = new MultipartConfigElement(
		   uploadDirectory.getAbsolutePath(),
		   maxUploadSizeInMb,
		   maxUploadSizeInMb * 2,
		   maxUploadSizeInMb / 2);
		registration.setMultipartConfig(multipartConfigElement);
	}
}

The class WebMvcConfiguration (shown in Listing JL-4) enables Spring MVC (with the @EnableWebMvc annotation) and plugs in an implementation of Spring’s MultipartResolver interface that delegates to the Servlet 3 javax.servlet.http.Part API. The MultipartResolver implementation is used to specify how Spring supports file uploads.

A very brief WebMvcConfiguration
@Configuration
@ComponentScan
@EnableWebMvc
class WebMvcConfiguration {

	@Bean
	public MultipartResolver multipartResolver() {
		return new StandardServletMultipartResolver();
	}

}

The @EnableWebMvc annotation provides quite a punch for such a puny annotation! Spring MVC will do a lot of things automatically at this point.

  • Spring MVC supports object-to-XML marshalling in REST services if a JAXB implementation is on the CLASSPATH.

  • Spring MVC supports validation of request payloads using JSR 303 annotations if an implementation like Hibernate Validator is on the CLASSPATH.

  • Spring MVC supports rendering ATOM or ROME feeds if the Rome library is on the CLASSPATH.

  • Spring MVC exposes every bean registered with the @Controller annotation on it as HTTP endpoints.

Our First Cut at a REST API

We know that we want to support a few different resource URIs to work with our data.

  • /users - The root URI for manipulating User records.

  • /users/{user} - Access and manipulate a specific User. {user} is a path variable expression that will be substituted at runtime for whatever values in a URI match this pattern.

  • /users/{user}/customers - Support traversal and modification of the customers collection.

  • /users/{user}/customers/{customer} - Support manipulation of individual customer records.

  • /users/{user}/photo - Support file upload and download for the profile photo for the given user.

The class shown in Listing JL-5 implements these endpoints.

A first cut at a REST endpoint supporting our CRM
package com.jl.crm.web;

import com.jl.crm.services.*;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.inject.Inject;
import java.util.ArrayList;

@Controller
@RequestMapping (value = "/users",
                 produces = MediaType.APPLICATION_JSON_VALUE)
class UserController {

	private CrmService crmService;

	@Inject
	void setCrmService(CrmService crmService) {
		this.crmService = crmService;
	}

	@RequestMapping (method = RequestMethod.DELETE, value = "/{user}")
	@ResponseBody User deleteUser(@PathVariable Long user) {
		return crmService.removeUser(user);
	}

	@RequestMapping (method = RequestMethod.GET, value = "/{user}")
	@ResponseBody User loadUser(@PathVariable Long user) {
		return crmService.findById(user);
	}

	@RequestMapping (method = RequestMethod.GET, value = "/{user}/customers")
	@ResponseBody CustomerList loadUserCustomers(@PathVariable Long user) {
		CustomerList customerResourceCollection = new CustomerList();
		customerResourceCollection.addAll(this.crmService.loadCustomerAccounts(user));
		return customerResourceCollection;
	}

	@RequestMapping (method = RequestMethod.GET, value = "/{user}/customers/{customer}")
	@ResponseBody Customer loadSingleUserCustomer(
		@PathVariable Long user,
		@PathVariable Long customer) {
		return crmService.findCustomerById(customer);
	}

	static class CustomerList extends ArrayList<Customer> {
	}
}

This is standard Spring MVC: requests are routed by the DispatcherServlet to methods on controller methods based on a matching process that draws on the configuration specified in each @RequestMapping annotation.

The various controller methods return a value. Most of them return a domain model type — Customer or User (or collections of either) — and have been annotated with @ResponseBody. When discovered on a method’s return value, @ResponseBody triggers the conversion of the returned value into an HTTP response by delegating to any of the configured HttpMessageConverter instances (supporting XML, JSON, etc.).

UserProfilePhotoController (shown in Listing JL-6) supports reading and writing user profile images.

UserProfilePhotoController receives uploaded file data and streams file data and ultimately depends on the MultipartResolver configured in Listing JL-4. Arguably, the file upload endpoint should be exposed over HTTP PUT, not POST, but POST works well enough and is easy to use from HTTP browsers. For practical reasons, you might consider exposing file endpoints as both POST and PUT.
package com.jl.crm.web;

import com.jl.crm.services.*;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.inject.Inject;
import java.net.URI;
import java.util.Collections;

@Controller
@RequestMapping (value = "/users/{user}/photo")
class UserProfilePhotoController {

	private CrmService crmService;

	@Inject
	void setCrmService(CrmService crmService) {
		this.crmService = crmService;
	}

	@RequestMapping (method = RequestMethod.POST)
	HttpEntity<Void> writeUserProfilePhoto(
		@PathVariable Long user, @RequestParam MultipartFile file) throws Throwable {
		// write it
		byte bytesForProfilePhoto[] = FileCopyUtils.copyToByteArray(file.getInputStream());
		MediaType mt = MediaType.parseMediaType(file.getContentType());
		this.crmService.writeUserProfilePhoto(user, mt, bytesForProfilePhoto);

		// tell the client what happened
		HttpHeaders httpHeaders = new HttpHeaders() ;
		URI uriOfUser = ServletUriComponentsBuilder.fromCurrentContextPath()
			.pathSegment( "/users/{user}" )
			.buildAndExpand(Collections.singletonMap("user", user))
			.toUri();
		httpHeaders.setLocation( uriOfUser );
		return new ResponseEntity<Void>(httpHeaders, HttpStatus.CREATED);
	}

	@RequestMapping (method = RequestMethod.GET)
	HttpEntity<byte[]> loadUserProfilePhoto(@PathVariable Long user) throws Throwable {
		// read it
		CrmService.ProfilePhoto profilePhoto = crmService.readUserProfilePhoto(user);
		if (null != profilePhoto){
			// send it back to the client
			HttpHeaders httpHeaders = new HttpHeaders();
			httpHeaders.setContentType(profilePhoto.getMediaType());
			return new ResponseEntity<byte[]>(profilePhoto.getPhoto(), httpHeaders, HttpStatus.OK);
		}
		throw new UserProfilePhotoReadException(user);
	}
}

Level Up with Hypermedia

Our API works fine, but there are few things that we can improve. First, our API is fragile in that any changes to the URLs would break clients connecting to the system. Additionally, there’s no way to signal to the client what entities are related to this entity and how. Clients must simply understand the domain and the relationships a priori.

One approach that is well defined in the canonical book REST in Practice is called HATEOAS. HATEOAS is part of REST-creator Roy Fielding’s uniform access principle. Roy clarifies these concepts in a few points in this 2008 blog.

  • "A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace."

  • "A REST API should never have “typed” resources that are significant to the client… The only types that are significant to a client are the current representation’s media type and standardized relation names…" That is to say: the client need only know how to work with ATOM/RSS, images, XLS, generic data encoded as JSON or XML, HTML, media, etc. No further knowledge about implementation types should be required.

  • The client should know about only one URI, the entry point (bookmark) URI. All other navigation should be discovered while interacting with the API. Navigation information is well conveyed using link entities, like this in XML: <link rel ="users" href="http://127.0.0.1:8080/users" /> or this in JSON: { "rel" : "users", "href" : "http://127.0.0.1:8080/users" }.

Spring HATEOAS layers on top of Spring MVC and makes it easy to incorporate support for these concerns in your Spring MVC applications. This brings us to Level 3 of the Richardson Maturity Model.

Supporting Hypermedia with Spring HATEOAS

To enable Spring HATEOAS, add the spring-hateoas dependencies to your CLASSPATH, and then update your WebMvcConfiguration class as shown in Listing JL-7.

This class installs Spring HATEOAS using the @EnableHypermediaSupport annotation and sets the default applicaton-wide content-type by overriding a callback method in the WebMvcConfigurationSupport class.
@Configuration
@ComponentScan
@EnableWebMvc
@EnableHypermediaSupport
class WebMvcConfiguration extends WebMvcConfigurationSupport {

	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer c) {
		c.defaultContentType(MediaType.APPLICATION_JSON);
	}

	@Bean
	public MultipartResolver multipartResolver() { .. }

}

Central to Spring HATEAOS is the concept of a org.springframework.hateoas.Resource - which is a wrapper for a response payload and navigation links that apply to that payload.

There are many competing formats for resource representation including Collection+JSON, HAL and Siren, and no real established winner (yet, though the trends seem to be favoring HAL). Spring HATEOAS supports its own representation, as well as HAL. Listing JL-8 demonstrates a resource (and associated links) using Spring HATEOAS' default representation format.

This is a sample representation as returned from a default Spring HATEOAS endpoint.
{
  "property1FromJsonPayload" : ..,
  "property2FromJsonPayload" : ..,
  "links" : [ { "rel" : "customers", "href" : "http://.../customers" }, {..}, .. ]
}

Listing JL-9 demonstrates a rewritten UserController that embraces Spring HATEOAS' Resource abstraction. The new implementaton delegates to resource assemblers to build the Resource instances, then returns them wrapped in HttpEntity instances. HttpEntity instances convey a response payload (the Resource) as well as HTTP response specifics like HTTP headers and status codes, key to building a idiomatic REST API.

The updated UserController that uses Spring HATEOAS Resource
import com.jl.crm.services.*;
import org.springframework.hateoas.*;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.inject.Inject;
import java.util.*;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

@Controller
@ExposesResourceFor (User.class)
@RequestMapping (value = ApiUrls.ROOT_URL_USERS, produces = MediaType.APPLICATION_JSON_VALUE  )
class UserController {

	private CrmService crmService;
	private UserResourceAssembler userResourceAssembler;
	private CustomerResourceAssembler customerResourceAssembler;

	@Inject
	void setCrmService(CrmService crmService) {
		this.crmService = crmService;
	}

	@Inject
	void setUserResourceAssembler(UserResourceAssembler userResourceAssembler) {
		this.userResourceAssembler = userResourceAssembler;
	}

	@Inject
	void setCustomerResourceAssembler(CustomerResourceAssembler customerResourceAssembler) {
		this.customerResourceAssembler = customerResourceAssembler;
	}

	@RequestMapping (method = RequestMethod.DELETE, value = ApiUrls.URL_USERS_USER)
	HttpEntity<Resource<User>> deleteUser(@PathVariable Long user) {
		Resource<User> userResource = userResourceAssembler.toResource(crmService.removeUser(user));
		return new ResponseEntity<Resource<User>>(userResource, HttpStatus.OK);
	}

	@RequestMapping (method = RequestMethod.GET, value = ApiUrls.URL_USERS_USER)
	HttpEntity<Resource<User>> loadUser(@PathVariable Long user) {
		Resource<User> resource = this.userResourceAssembler.toResource(crmService.findById(user));
		return new ResponseEntity<Resource<User>>(resource, HttpStatus.OK);
	}

	@RequestMapping (method = RequestMethod.GET, value = ApiUrls.URL_USERS_USER_CUSTOMERS)
	HttpEntity<Resources<Resource<Customer>>> loadUserCustomers(@PathVariable Long user) {
		Collection<Resource<Customer>> customerResourceCollection = new ArrayList<Resource<Customer>>();
		for (Customer c : this.crmService.loadCustomerAccounts(user)) {
			customerResourceCollection.add(customerResourceAssembler.toResource(c));
		}
		Resources<Resource<Customer>> customerResources = new Resources<Resource<Customer>>(customerResourceCollection);
		customerResources.add(linkTo(methodOn(UserController.class).loadUserCustomers(user)).withSelfRel());
		return new ResponseEntity<Resources<Resource<Customer>>>(customerResources, HttpStatus.OK);
	}

	@RequestMapping (method = RequestMethod.GET, value = ApiUrls.URL_USERS_USER_CUSTOMERS_CUSTOMER)
	HttpEntity<Resource<Customer>> loadSingleUserCustomer(@PathVariable Long user, @PathVariable Long customer ) {
		Resource<Customer> customerResource = customerResourceAssembler.toResource( this.crmService.findCustomerById(customer ));
		return new ResponseEntity<Resource<Customer>>(customerResource, HttpStatus.OK);
	}
}

The ResourceAssembler implementations adapt our response payloads into Resource instances; they assemble them. Shown in Listing JL-10 is the resource assembler for Customer entities.

The ResourceAssembler that adapts Customer entities to Resource<Customer>> instances.
import com.jl.crm.services.*;
import org.springframework.hateoas.*;
import org.springframework.stereotype.Component;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

@Component
class CustomerResourceAssembler implements ResourceAssembler<Customer, Resource<Customer>> {

	public static final String USER_REL = "user";
	private Class<UserController> controllerClass = com.jl.crm.web.UserController.class;

	@Override
	public Resource<Customer> toResource(Customer customer) {
		long userId = customer.getUser().getId();
		customer.setUser(null);
		Resource<Customer> customerResource = new Resource<Customer>(customer);
		Link selfLink = linkTo(methodOn(controllerClass).loadSingleUserCustomer(userId, customer.getId())).withSelfRel();
		Link userLink = linkTo(methodOn(controllerClass).loadUser( userId)).withRel(USER_REL);
		customerResource.add(selfLink);
		customerResource.add(userLink);
		return customerResource;
	}
}

The ResourceAssembler creates a Resource object with a payload and attaches Link instances. Java doesn’t (yet) support method references, but Spring HATEOAS’s ControllerLinkBuilder can create a dynamic proxy of the target controller method’s containing class. When a method on this proxy is invoked, its @RequestMapping mapping information is extracted and used to generate the URI in the link. In this way, configuration (like URI mappings) is only ever specified once. It’s OK to pass in null for all the arguments of the controller method except for the arguments that contribute to the URI itself, such as @PathVariable-annotated arguments. You should pass in the real value there as it’ll contribute to the link’s URI.

The Link builder fluid DSL captures both the URI (through the method-capturing proxy) and a rel value, a string that describes the relevance of the URI for any client working with this resource. A resource should only return Link navigations that are valid for the resource in its current state. It would make no sense, for example, to return a Link to obtain a refund on a resource for an order that hasn’t been paid for yet.

Interfacing with a Hypermedia REST API with the Spring REST Shell

From the documentation, `the goal of the `rest-shell is to make it much easier to work with HATEOAS HTTP-based REST services by providing a command shell interface that keeps track of where you are in the HTTP resource hierarchy and allowing you to specify URLs by short, relative paths, or by rel reference.'' Releases are available on the project’s GitHub page and via the brew package manager on OSX (brew install rest-shell). Listing JL-11 reproduces a session spent in the shell interacting with our hypermedia-powered API.

A session spent talking to the CRM REST API from the Spring rest-shell.
Joshuas-MacBook-Pro:the-spring-rest-stack jlong$ rest-shell

 ___ ___  __ _____  __  _  _     _ _  __
| _ \ __/' _/_   _/' _/| || |   / / | \ \
| v / _|`._`. | | `._`.| >< |  / / /   > >
|_|_\___|___/ |_| |___/|_||_| |_/_/   /_/
1.2.0.RELEASE

Welcome to the REST shell. For assistance hit TAB or type "help".
http://localhost:8080:> follow users
http://localhost:8080/users:> follow 21
http://localhost:8080/users/21:> get
> GET http://localhost:8080/users/21/

< 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Wed, 26 Jun 2013 11:39:16 GMT
<
{
  "links" : [ {
    "rel" : "self",
    "href" : "http://localhost:8080/users/21"
  }, {
    "rel" : "customers",
    "href" : "http://localhost:8080/users/21/customers"
  }, {
    "rel" : "photo",
    "href" : "http://localhost:8080/users/21/photo"
  } ],
  "id" : 21,
  "firstName" : "Josh",
  "profilePhotoMediaType" : "image/jpeg",
  ..
}
http://localhost:8080/users/21:> discover
rel          href
=====================================================
self         http://localhost:8080/users/21
customers    http://localhost:8080/users/21/customers
photo        http://localhost:8080/users/21/photo

http://localhost:8080/users/21:> follow customers
http://localhost:8080/users/21/customers:> get
> GET http://localhost:8080/users/21/customers/

< 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Wed, 26 Jun 2013 11:39:33 GMT
<
{
  "links" : [ {
    "rel" : "self",
    "href" : "http://localhost:8080/users/21/customers"
  } ],
  "content" : [ {
    "links" : [ {
      "rel" : "self",
      "href" : "http://localhost:8080/users/21/customers/76"
    }, {
      "rel" : "user",
      "href" : "http://localhost:8080/users/21"
    } ],
    "id" : 76,
    "signupDate" : 1371091042082,
    "firstName" : "Michael",
    "lastName" : "Chang",
    "databaseId" : 76
  },  ...
}
http://localhost:8080/users/21/customers:>

In the session, I navigate URIs by following the link elements. Commands like get and post support issuing the HTTP verbs of the same name. The rest-shell can also discover relevant navigation links if it understands the representation.

Spring Data REST

The UserProfilePhotoController is doing about as little work as possible to satisfy the requirements of the endpoint it exposes. I’m not certain there’s really much that could be done to make this simpler. Our UserController, on the other hand, deals a lot with the state management of User and Customer records — all that has to do with storing, updating, reading or removing them. When we implement this behavior — as often as not — we simply unwrap the request and forward it on to the injected crmService, which in turn — as often as not — forwards the request on to the injected UserRepository instance. So, why not cut out the middle men?

With Spring Data REST, we can do just that! Spring Data REST makes it dead simple to expose RESTful endpoints that work with Spring Data repository instances.

Add Spring Data’s pre-packaged Java configuration class, RepositoryRestMvcConfiguration to the array of configuration classes returned in the getServletConfigClasses method of CrmWebApplicationInitializer. Listing JL-12 shows the updated code.

The updated CrmWebApplicationInitializer
...
public class CrmWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
	...
	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class<?>[]{
   		// provided by Spring Data REST
			RepositoryRestMvcConfiguration.class,
			WebMvcConfiguration.class};
	}

	...

The RepositoryRestMvcConfiguration class automatically detects Spring Data repositories that are annotated with @RestResource and exports them as REST resources. Add the annotation to the UserRepository interface and CustomerRepository interfaces, as shown in Listing JL-13.

Export your Spring Data REST endpoints with @RestResource.
@RestResource (path = "users", rel = "users")
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
	...
}

With Spring Data REST, all of the functionality in our existing UserController is redundant! Disable the UserController class by commenting out the @Controller annotation on the UserController.

Run the updated application ( hateoas-data) and you’ll see the usual endpoints for /users, /users/{user}, /customers, /customers/{customer} as we had from before. But wait, there’s more! The only endpoints that Spring Data REST didn’t make redundant are the endpoints concerned with manipulating the profile photo. You’ll notice that you get a lot more out of the box than that when you first launch the browser.

/ returns a sort of useful index page of the possible resources and the collections they manage.

Spring Data REST can produce a schema for each resource, for example, http://localhost:8080/users/schema (Listing JL-14).

This is an excerpted part of the schema generated by Spring Data REST for the users resource.
{
"links" : [ {
    "rel" : "users.user.customers",
    "href" : "http://localhost:8080/users/%257Bid%257D/customers"
} ],
"name" : "com.jl.crm.services.User",
"properties" : {
    "id" : {
	    "type" : "long",
	    "description" : null,
	    "required" : false
    },
    "lastName" : {
	    "type" : "string",
	    "description" : null,
	    "required" : false
    },
    ...

Spring Data REST automatically exports repository finder methods as HTTP endpoints. These finder endpoints simply expose searches from a repository over HTTP. For example, the repository query method findUsersByFirstNameOrLastNameOrUsername is exposed as http://localhost:8080/users/search/findUsersByFirstNameOrLastNameOrUsername?username=joshlong&lastName=long&firstName=josh. I’m not sure you’d want to expose these finder endpoints to external consumers of your API, but it’s a nice feature anyway.

Spring Data REST respects Spring Data’s concepts of paging and sorting. As an example, http://localhost:8080/users/?sort=firstName&page=1 would return all the users, sorted by the firstName property.

Customizing Spring Data REST

Spring Data REST has a lot of extension points. For example, it is easy to pre- and post-process entity state while persisting entities with @RepositoryEventHandler-annotated beans. Spring will invoke any annotated callback-methods for the appropriate events in the entity lifecycle. This gives us a chance to affect, or validate, the state of the entity before its persisted (Listing JL-15).

This an implementation that responds to lifecycle events for Customer entities. It responds to events for the Customer entity.
import com.jl.crm.services.*;
import org.apache.commons.logging.*;
import org.springframework.data.rest.repository.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
@RepositoryEventHandler (Customer.class)
public class CustomerEventHandler {

	private Log logger = LogFactory.getLog(getClass());

	@HandleBeforeCreate
	public void handleBeforeCreate(Customer customer) {
		if (StringUtils.hasText(customer.getFirstName())
			&& StringUtils.hasText(customer.getLastName())
			&& customer.getUser() != null){
			if (customer.getSignupDate() == null){
				customer.setSignupDate(new java.util.Date());
			}
		}
		else {
			throw new CustomerWriteException(customer, new RuntimeException("you must specify a 'firstName' and a 'lastName' and a valid user reference."));
		}
		logger.debug("handling before create for " + customer.toString());
	}

	@HandleAfterSave
	public void handleAfterSave(Customer customer) {
		logger.debug("saved customer #" + customer.getId());
	}

	@HandleAfterDelete
	public void handleAfterDelete(Customer customer) {
		logger.debug("deleted customer #" + customer.getId());
	}
}

Spring Data REST returns well-formatted Spring HATEOAS-powered JSON representations, complete with useful link elements for the associations on the entity. Automatically…​ That’s pretty darned good if you think about it!

Delete the UserController, as it is now obsolete. This impacts all of the link instances we created earlier, as they depended on a proxy that matches the UserController interface. Instead, use the EntityLinks link-builder. It supports common conventions for entities. For example: given the entity User, it’s expected that /users maps to the collection of User entities, and that /users/{id} will map to a single instance of an entity (as identified by an ID).

The ResourceAssembler implementations are no longer needed and so can be deleted. The only thing remaining is our UserProfilePhotoController, which creates link elements. Creating link elements is something common enough that it makes sense to extract it out to a common component, like the UserLinks class shown in Listing JL-16.

UserLinks class codifies the creation of commonly used Link types.
import com.jl.crm.services.User;
import org.springframework.hateoas.*;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.inject.Inject;

@Component
public class UserLinks {

	private static final String PHOTO = "photo";
	private static final String PHOTO_REL = "photo";
	private static final String CUSTOMER = "customers";
	private static final String CUSTOMER_REL = "customers";
	private final EntityLinks entityLinks;

	@Inject
	UserLinks(EntityLinks entityLinks) {
		Assert.notNull(entityLinks, "EntityLinks must not be null!");
		this.entityLinks = entityLinks;
	}

	Link getSelfLink(User user) {
		return this.entityLinks.linkForSingleResource(User.class, user.getId())
			.withSelfRel();
	}

	Link getCustomersLink(User user) {
		return this.entityLinks.linkForSingleResource(User.class, user.getId())
			.slash(CUSTOMER).withRel(CUSTOMER_REL);
	}

	Link getPhotoLink(User user) {
		return this.entityLinks.linkForSingleResource(User.class, user.getId())
			.slash(PHOTO).withRel(PHOTO_REL);
	}

}

Now, revise the UserProfilePhotoController class to use the UserLinks bean, as shown in Listing JL-17.

The updated UserProfilePhotoController using the UserLinks bean
@Controller
@RequestMapping (value = "/users/{user}/photo")
public class UserProfilePhotoController {
	...
	private UserLinks userLinks;

	@Inject
	void setUserLinks(UserLinks userLinks) {
		this.userLinks = userLinks;
	}

	@RequestMapping (method = RequestMethod.POST)
	HttpEntity<Void> writeUserProfilePhoto(@PathVariable User user, @RequestParam MultipartFile file) throws Throwable {
		...
		Link photoLink = this.userLinks.getPhotoLink(user);
		Link userLink = this.userLinks.getSelfLink(user);
		Links wrapperOfLinks = new Links(photoLink, userLink);

		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.add("Link", wrapperOfLinks.toString());
		httpHeaders.setLocation(URI.create(photoLink.getHref()));

		return new ResponseEntity<Void>(httpHeaders, HttpStatus.ACCEPTED);
	}
	...
}

Web Application Security with Spring Security

HTTP’s flexibility and simplicity can be deceptive. Just how does one secure a REST service? The answer is as always: it depends. What do you mean by authorization? Encryption? Authentication? Most of the concerns we have are already addressed and supported by the open web and Spring Security. Let’s look at the oauth module, which introduces security to the REST service we built up in our last installment.

HTTP Secure (HTTPS) is a communications protocol for secure communication on top of the SSL/TLS protocol. It protects against man-in-the-middle attacks and attempts to ensure the integrity of the payload communicated between two parties. You can easily set this feature up on most web servers as well as most cloud platforms. It’s fairly easy to setup SSL/TLS with Apache Tomcat or with a cloud platform like AppFog or Heroku. What little there is to do at the application level, Spring Security ably supports. HTTPS is something configured at the runtime-level, however, so we won’t explore this topic any further in this post. At the time of this writing, however, it was discovered that HTTPS, particularly older versions, has been compromised.

Username and Password Based Authentication

HTTP Basic Authentication provides a very simple way to handle access control. Another approach to securing an application is to simply use a login form on the application somewhere, typically in conjunction with something like a server-side session. Neither HTTP Basic Authentication nor sign-in forms provide any guarantee of confidentiality of transmitted content. They’re both best used in conjunction with SSL/TLS.

Installing Spring Security in our Servlet Application

We’ll use the Spring Security Java Configuration support to configure our application’s security rules. Spring Security is ultimately implemented in a web application as a javax.filter.Filter object configured in the servlet container. The Spring Security filter should be the front-line of the web application, inspecting every request as it goes through. To automate the installation of all of these components, Spring Security supports a custom WebApplicationInitializer subclass called AbstractSecurityWebApplicationInitializer. You simply extend the class and fill out the abstract template methods with information that it needs. See Listing JL-1 for an example.

An implementation of AbstractSecurityWebApplicationInitializer to secure our Spring application
package com.jl.crm.web;

import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.multipart.support.MultipartFilter;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

import javax.servlet.ServletContext;

public class CrmSecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

	@Override
	protected String getDispatcherWebApplicationContextSuffix() {
		return AbstractDispatcherServletInitializer.DEFAULT_SERVLET_NAME;
	}

 	@Override
	protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
		insertFilters(servletContext,
		   new HiddenHttpMethodFilter(),
		   new MultipartFilter() ,
		   new OpenEntityManagerInViewFilter());
	}

	@Override
	protected boolean enableHttpSessionEventPublisher() {
		return true;
	}
}

This WebApplicationInitializer instance is additive - it will be included in addition to any other initializers present. As you might imagine, this is a very convenient way for modules to provide out-of-the-box integration with the Servlet container. CrmSecurityApplicationInitializer implements the abstract callback method, afterSpringSecurityFilterChain which gives the client a chance to insert javax.filter.Filter instances into the Filter chain after Spring Security’s filter is installed. As we’re installing the Filter instances here, we can remove the corresponding entries from our CrmWebApplicationInitializer.

Configuring A Sign-In Form with Spring Security

The Spring Security Java configuration class presented in Listing JL-2 demonstrates how to setup form-based signin and signout functionality, and how to specify which URIs should be protected by that signin form. Our sample application uses H2, the embedded Java database. The H2 database console itself uses HTML frame elements and so runs afoul of a particular type of protection that Spring Security installs by default. The configurer object returned from the headers() method allows us to override the default HeaderWriter instances to avoid this problem.

Spring Security Java configuration
@Configuration
@EnableWebSecurity
class SecurityConfiguration extends WebSecurityConfigurerAdapter {

	@Inject private UserDetailsService userDetailsService;

	@Override
	protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(this.userDetailsService);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {


		http.formLogin()
				  .loginPage("/crm/signin.html")
				  .loginProcessingUrl("/signin")
				  .defaultSuccessUrl("/crm/welcome.html")
				  .failureUrl("/crm/signin.html?error=true")
				  .usernameParameter("username")
				  .passwordParameter("password")
				  .permitAll(true);

		http.headers()
				  .addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN))
				  .addHeaderWriter(new XContentTypeOptionsHeaderWriter())
				  .addHeaderWriter(new XXssProtectionHeaderWriter())
				  .addHeaderWriter(new CacheControlHeadersWriter())
				  .addHeaderWriter(new HstsHeaderWriter());

		http.logout().logoutUrl("/signout").deleteCookies("JSESSIONID");


		// nb: the H2 administration console should *not* be left exposed.
		// comment out the mapping path below so that it requires an authentication to see it.
		String[] filesToLetThroughUnAuthorized =
 		  { H2EmbeddedDatbaseConsoleInitializer.H2_DATABASE_CONSOLE_MAPPING, "/favicon.ico" };

		http.authorizeRequests()
				  .antMatchers(filesToLetThroughUnAuthorized).permitAll()
				  .anyRequest().authenticated();
	}
}

Teaching Spring Security New Tricks

The userDetailsService instance registered in registerAuthentication is an implementation of Spring Security’s UserDetailsService interface. This interface is a strategy interface with numerous other backend implementations available supporting technologies like LDAP, Active Directory, pam, SAML, etc.

Our system already has a notion of a user. It’s easy enough to adapt Spring Security to our user management logic with a custom implementation of UserDetailsService. This interface is integral to Spring Security. Listing JL-3 demonstrates how we wrap and adapt our existing user-management logic to fit in the Spring Security framework. The implementation mostly delegates what it can to our underlying service, and hard codes the rest as our application isn’t all that involved at the moment. Note, particularly, the scopes and role harccoded for the resulting UserDetails instances as we’ll refer to those again later.

A caption for the code
package com.jl.crm.services.security;

import com.jl.crm.services.CrmService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.*;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.inject.Inject;
import java.util.*;

@Component ("userService")
public class CrmUserDetailsService implements UserDetailsService {

	private CrmService crmService;

	@Inject
	public void setCrmService(CrmService crmService) {
		this.crmService = crmService;
	}

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		com.jl.crm.services.User user = crmService.findUserByUsername(username);
		return new CrmUserDetails(user);
	}

	@SuppressWarnings("serial")
	public static class CrmUserDetails implements UserDetails {

 		public static final String SCOPE_READ = "read";

		public static final String SCOPE_WRITE = "write";

 		public static final String ROLE_USER = "ROLE_USER";

		private Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();

		private com.jl.crm.services.User user;

		public CrmUserDetails(com.jl.crm.services.User user) {
			Assert.notNull(user, "the provided user reference can't be null");
			this.user = user;
			for (String ga : Arrays.asList(ROLE_USER, SCOPE_READ, SCOPE_WRITE)) {
				this.grantedAuthorities.add(new SimpleGrantedAuthority(ga));
			}
		}

		@Override
		public Collection<? extends GrantedAuthority> getAuthorities() {
			return this.grantedAuthorities;
		}

		@Override
		public String getPassword() {
			return user.getPassword();
		}

		@Override
		public String getUsername() {
			return user.getUsername();
		}

		@Override
		public boolean isAccountNonExpired() {
			return isEnabled();
		}

		@Override
		public boolean isAccountNonLocked() {
			return isEnabled();
		}

		@Override
		public boolean isCredentialsNonExpired() {
			return isEnabled();
		}

		@Override
		public boolean isEnabled() {
			return user.isEnabled();
		}

		public com.jl.crm.services.User getUser() {
			return this.user;
		}
	}
}

Putting Out the welcome.html Mat

With all of this in place, any unauthorized HTTP request will be routed to the signin view (signin.html). When authentication is successful, the client is then redirected to /crm/welcome.html. JL-4 presents the @Controller handler for the signin and welcome views, both of which are used in the signin process.

@Controller that simply returns views for /crm/signin.html and /crm/welcome.html. These could, as easily, be view controllers.
package com.jl.crm.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class PageControllers {

	@RequestMapping ("/crm/welcome.html")
	public String welcome() {
		return "welcome";
	}

	@RequestMapping ("/crm/signin.html")
	public String signin() {
		return "signin";
	}

}

The markup for each view (shown in Listing JL-5 and JL-6) is simple, fairly typical HTML (again in the interest of conciseness and transparency). You could easily use Spring Mobile to conditionally render experiences tailored to mobile, desktop, or tablet clients. In this case, the markup is so simple that it looks equally boring on all platforms, so we didn’t need Spring Mobile.

The signin.jsp page
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %>

<html>
<head>
    <title>
        Sign In
    </title>
</head>
<body>
<h1> Sign In</h1>

<div>${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
<authz:authorize ifAllGranted="ROLE_USER">
    <A href="${pageContext.request.contextPath}/signout">Sign Out</A>
</authz:authorize>

<authz:authorize ifNotGranted="ROLE_USER">
    <p>
        Please enter your username and password to log into the application.
    </p>
    <form method="post" action="${pageContext.request.contextPath}/signin">
        <DIV>
            <label style="width: 100px; display: inline-block;"  class="control-label" for="username"> User Name: </label> <br/>
            <input id="username" name="username" type="text"/>
        </DIV>
        <DIV>
            <label style="width: 100px; display: inline-block"  class="control-label"  for="password"> Password: </label> <br/>
            <input class="input-xlarge" id="password" name="password" type="password"/>
        </DIV>
        <input type="submit"/>
    </form>
</authz:authorize>
</body>
</html>
The welcome.jsp page
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
    <title> Greetings! </title>
</head>
<body>
<sec:authorize ifAllGranted="ROLE_USER">
    <h1>Hi, <sec:authentication property="principal.username"/>!</h1>
    <a href="${pageContext.request.contextPath}/signout">Sign Out</a>.
</sec:authorize>
<sec:authorize ifNotGranted="ROLE_USER">
    <h1>Hello, stranger.</h1>
</sec:authorize>
</body>
</html>

Accessing the Currently Authenticated User from REST

First things first: we don’t want users reading other users' information. We can deny access to the /users/ endpoint simply by adding another restriction in Spring Security.

Deny access to the /users/* endpoint in the SecurityConfiguration class.
    ...
    http.authorizeRequests()
        .antMatchers(filesToLetThroughUnAuthorized).permitAll()
        .antMatchers("/users/*").denyAll()
        .anyRequest().authenticated();
    ...

We still need a way to access the currently authenticated user. As a convenience, it’s useful to implement an endpoint to return the currently authenticated user, which we know we can get at from the Spring Security context. This listing demonstrates one possible implementation for a REST endpoint situated at /user.

An implementation of /user
package com.jl.crm.web;

import com.jl.crm.services.User;
import org.springframework.hateoas.*;
import org.springframework.http.*;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.inject.Inject;
import java.util.*;

@Controller
public class CurrentUserController {

	private UserLinks userLinks;

	@RequestMapping (value = "/user", method = RequestMethod.GET)
	public HttpEntity<Resource<User>> currentUser(@ModelAttribute User self) {
		List<Link> linkList = new ArrayList<Link>();
		linkList.add(this.userLinks.getSelfLink(self));
		linkList.add(this.userLinks.getPhotoLink(self));
		linkList.add(this.userLinks.getCustomersLink(self));
		UserResource userResource = new UserResource(self, linkList);
		return new ResponseEntity<Resource<User>>(userResource, HttpStatus.OK);
	}

	@Inject
	public void setUserLinks(UserLinks userLinks) {
		this.userLinks = userLinks;
	}

	static class UserResource extends Resource<User> {
		public UserResource(User content, Iterable<Link> links) {
			super(content, links);
		}
	}
}

The controller injects the current User as a @ModelAttribute into the controller method argument. As you might imagine, having the current authenticated user available can be a handy thing in any complicated system. We could already simply inject the current Spring Security Authentication object then dereference the principal, but this involves an ugly cast that would end up littered throughout the code. Instead, it’s easy enough to put that code in a Spring MVC @ControllerAdvice component. A @ControllerAdvice instance looks and feels very much like a controller in that it may register exception handlers (with the @ExceptionHandler annotation), and model attributes (with the @ModelAttribute annotation), etc., in a central place and is involved in every request. Here’s an implementation - SecurityControllerAdvice - that sets up a @ModelAttribute for the currently authenticated users.

An implementation of /user
package com.jl.crm.web;

import com.jl.crm.services.*;
import com.jl.crm.services.security.CrmUserDetailsService;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import javax.inject.Inject;

@ControllerAdvice
public class SecurityControllerAdvice {

	@Inject private CrmService service;

	@ModelAttribute
	public User currentUser(Authentication authentication) {
		if (null == authentication){
			return null;
		}
		CrmUserDetailsService.CrmUserDetails crmUserDetails = (CrmUserDetailsService.CrmUserDetails) authentication.getPrincipal();
		long userId = crmUserDetails.getUser().getId();
		return this.service.findById(userId);
	}
}

With all of this in place, launch the application and then authenticate yourself using any of the users and passwords in the user_account table in our schema. By default, the application preloads data from crm-schema-h2.sql.

Scaling Web Security with Spring Security OAuth

Username and password-based access control works well enough for trusted clients. If you are the only person who knows your (hopefully randomized) password, then the chances that a malicious client might afflict your account are far lower. A password authentication scheme is a simple way to identify that a client is who it says it is. However, this scheme starts to fall apart in a world where multiple clients may interact with a service provider on your behalf. This is typical, for example, when using service-provider clients for Netflix, Facebook, Twitter, and Google+ clients across phones, desktop browsers, tablets, and TVs.

For a specific example, let’s look at the Facebook application ecosystem. Facebook opened up the Facebook object graph through its REST APIs and opened the door to a slew of application developers that wanted to deliver applications (``Sign In with Facebook'') and games integrated with your Facebook experience and data. Some of these API clients were better than others. Some were downright abusive! However, no matter how abusive a client got, it could never lock you out of your own account because it never had access to your password. Instead, clients authenticate with Facebook and other service providers using a system called OAuth.

You’ll recognize the telltale signs of OAuth when you use a Sign In with Facebook'' or Sign In with Twitter'' button on any number of web properties. Invariably, these buttons launch Facebook.com, prompt the user to sign in (if not already signed in) (as shown in Figure JL-1) and then prompt the user to authorize the permissions required by the application (shown in Figure JL-2). Once permission is granted, you’ll end up back where you started, but the application will have what it needs to authenticate you and sign you in. Convenient, eh?

The Facebook signin page.
Figure 1. The Facebook signin page.
The Facebook approval page.
Figure 2. The Facebook approval page.
The Facebook applications console.
Figure 3. The Facebook applications console.

When a user authorizes the application and requested permissions, Facebook ultimately redirects the client to the requesting application and conveys in that request an accessToken. The accessToken is like a session cookie, and tells the server which client is connecting on behalf of the authenticated user. Additionally, Facebook has the ability to track which applications are installed in an application console (shown in Figure JL-3). The console is powerful: here, a user may centrally control (including revoke) access to any and all clients, selectively.

A full discussion of OAuth is out of scope here, but you might check out Dr. David Syer’s many amazing posts on the subject from the Cloud Foundry blog in 2012. OAuth provides a few things we’ll desire as part of our system.

So: a username and password scheme certifies that a request is being made by a user. OAuth certifies that the request being made has the permission to do so from the user. That’s close enough to certifying the user for most applications that OAuth works with.

Introducing Spring Security OAuth

Integrating OAuth into our application is a snap thanks to Spring Security OAuth. Spring Security OAuth introduces the concept of a client - a logical notion composed of the authenticated user, a unique identifier, certain permissions (or scopes) that the client is permitted, and the type of OAuth connectivity supported by that client. OAuth supports varying levels of security, and can force the client to go through more hoops to increase the confidence in the security provided. We’re going to assume the common, but not exclusive, case of working with an Android mobile client. Spring Security OAuth needs information about which clients will connect.

The listing below demonstrates the things added to the Spring Security Java configuration class - SecurityConfiguration - to support Spring Security OAuth.

This configuration class extends from OAuth2ServerConfigurerAdapter.
@Configuration
@EnableWebSecurity
class SecurityConfiguration extends OAuth2ServerConfigurerAdapter {

	private final String applicationName = ServiceConfiguration.CRM_NAME;
	@Inject private UserDetailsService userDetailsService;
	@Inject private DataSource dataSource;

	@Override
	protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {

	auth.apply(new InMemoryClientDetailsServiceConfigurer())
				  .withClient("android-crm")
				  .resourceIds(applicationName)
				  .scopes("read", "write")
				  .authorities("ROLE_USER")
				  .authorizedGrantTypes("authorization_code", "implicit", "password")
				  .secret("123456");

		auth.userDetailsService(userDetailsService);
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		// ..

		http.apply(new OAuth2ServerConfigurer())
				  .tokenStore(new JdbcTokenStore(this.dataSource))
				  .resourceId(applicationName);

		// ..

	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return NoOpPasswordEncoder.getInstance();
	}

	@Bean
	public TextEncryptor textEncryptor() {
		return Encryptors.noOpText();
	}
}

The first thing you’ll notice in Listing JL-8 is that the class extends OAuth2ServerConfigurerAdapter instead of WebSecurityConfigurerAdapter. The two beans - passwordEncoder and textEncryptor - below are just no-op implementations of two APIs that Spring Security requires.

In Spring Security OAuth, the ClientDetailsService manages the information about which clients might connect, and how. To simplify setup, register an InMemoryClientDetailsServiceConfigurer in the registerAuthentication method. You could as easily plugin an implementation backed by a database.

Here, we have only one client in mind, so we’ll configure a client (android-crm) that requests two scope's, read and write. Scopes are arbitrary strings. In the Facebook world, they correspond to things an API client might want to do, like post to your wall, access your email address, etc. In the Spring Security world, they line up with the grantedAuthorities returned from the CRM-specific UserDetails instances vended by our CrmUserDetailsService instance. In our application, these roles are arbitrary, hard-coded Strings. You can, of course, be as granular as you like in using this facility to limit access to certain parts of your application.

Once the user has successfully authenticated, an OAuth service sends back a token to be transmitted on all subsequent API requests. The service needs to remember that token. Spring Security OAuth delegates this chore to a TokenStore-implementation, in this case a JdbcTokenStore.

When users run through the OAuth dance with our application, on the other hand, they’ll be presented with a page to confirm what scopes the user authorizes to the client. We must provide this page ourselves. The code in Listing JL-9 shows an implementation that prepares information and then renders access_confirmation. The view itself - access_confirmation - is shown below.

A @Controller to respond to the /oauth/confirm_access callback in the OAuth dance
package com.jl.crm.web;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.web.WebAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.inject.Inject;
import javax.servlet.http.*;
import java.util.Map;

@Controller
@SessionAttributes ("authorizationRequest")
public class OAuthController {

	private ClientDetailsService clientDetailsService;

	@RequestMapping ("/oauth/confirm_access")
	public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest httpServletRequest) throws Exception {
		AuthorizationRequest clientAuth = (AuthorizationRequest) model.remove("authorizationRequest");
		ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
		model.put("auth_request", clientAuth);
		model.put("client", client);

		HttpSession httpSession = httpServletRequest.getSession(false);
		if (httpSession != null){
			Object exception = httpSession.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
			if (exception != null && exception instanceof AuthenticationException){
				model.put("exception", exception);
			}
		}
		return new ModelAndView("access_confirmation", model);
	}

	@Inject
	public void setClientDetailsService(ClientDetailsService clientDetailsService) {
		this.clientDetailsService = clientDetailsService;
	}
}
The access_confirmation view. Prompts the user to grant the client permissions, or not.
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>
<head>
    <title>
        Please authorize the client "${client.clientId}" to act on your behalf.
    </title>
</head>
<body>
<h1> Access Confirmation
</h1>


<authz:authorize ifAllGranted="ROLE_USER">
    <p>
        Do you approve <strong> ${client.clientId} </strong> with the following permissions?
    </p>
    <form action="${pageContext.request.contextPath}/oauth/authorize" method="post">
        <button type="submit" name="authorize" value="${buttonLabel}">Yes</button>
        <input name="user_oauth_approval" value="true" type="hidden"/>
        <div>
            <strong>Yes</strong>, I authorize <strong>${client.clientId}</strong> to act on my behalf. Your password will <EM>not</EM> be shared with the client.
        </div>
    </form>
    <form action="${pageContext.request.contextPath}/oauth/authorize" method="post">
        <button type="submit" name="deny" value="${buttonLabel}">
            No
        </button>
        <input name="user_oauth_approval" value="false" type="hidden"/>
        <div>
            <strong>No</strong>, I do not authorize <strong>${client.clientId}</strong> to act on my behalf.
        </div>
    </form>
</authz:authorize>
</body>
</html>
The CRM signin page.
Figure 4. The CRM signin page.
The CRM approval page.
Figure 5. The CRM approval page.

Spring’s REST Client, the RestTemplate

The RestTemplate - in core Spring - is the pillar of Spring’s REST story on the client. The RestTemplate reduces common HTTP operations to one liners like JdbcTemplate does JDBC operations. The RestTemplate features the ability to plugin HTTP message conversion using the same HttpMessageConverter hierarchy available on the service-side for REST content negotiation. The RestTemplate also supports an interceptor model for processing all requests. This makes it easy to handle things like security in an single place.

Easy OAuth Clients with Spring Social

The RestTemplate is powerful, and makes a great foundation for any REST client, but it’s too low-level for something like consuming OAuth services. As we’ve just seen, implementing OAuth does introduce some complexity that straight REST calls don’t have. This complexity can’t be avoided either. After all, between OAuth 1.0, 1.0.a, and 2.0, and the various inconsistencies among their implementations (OAuth 2.0 isn’t even really a specification - it’s more like a framework!), building an OAuth client can be quite challenging. There are enough popular service providers out there with APIs exposed using OAuth that simply ignoring OAuth isn’t an option either! Spring Social fills the gap.

Spring Social is an extension of the Spring Framework that allows you to connect your applications with Software-as-a-Service (SaaS) providers, like Facebook and Twitter. As part of that charge, it makes it dead simple to build OAuth clients by building on top of the RestTemplate to support transparent, secured REST calls, and manage HTTP callbacks.

This core support for OAuth authorization and authentication underpins a rich catalog of API bindings for popular service-providers like Twitter, Facebook, LinkedIn, and more. You should take a quick look at all the repositories on the SpringSource GitHub repository to see the API bindings that SpringSource maintains directly. I, however, think the (very incomplete!) list of some of the third party API bindings is a far more interesting list!

Writing a Spring Social CRM Client

Spring Social is probably most often going to be used inside of a web container. We’re going to build our Spring Social CRM client to be used in an Android client, outside of a web container. OAuth works best when the client to the service provider is itself a web application that can receive callbacks. Spring Social, when used inside of a Spring MVC web application, is almost trivial to setup. That scenario is very well documented elsewhere, and what we really need is a client that can run in Android, so we’ll cover that instead.

All Spring Social clients share basically the same arrangement as the one we’ll implement here. Once you understand how to exercise one, then - while accounting for some small variations between OAuth versions - you’ll use the API in roughly the same way thanks to Spring Social’s abstractions.

The CrmOperations interface

A Spring Social binding centers around an API interface and an implementation. The interface for the CRM is, unsurprisingly, CrmOperations, and the implementation CrmTemplate. This follows a Spring-project convention: JdbcOperations and JdbcTemplate, JmsOperations and JmsTemplate, FacebookOperations and FacebookTemplate, etc. Our client interface is shown in Listing JL-11.

The CrmOperations is a client-side mirror to our REST API.
package com.jl.crm.client;

import org.springframework.http.MediaType;

import java.net.URI;
import java.util.*;

public interface CrmOperations {

	User currentUser();

	Customer loadUserCustomer(Long id);

	Customer createCustomer(String firstName, String lastName, Date signupDate);

	Collection<Customer> loadAllUserCustomers();

	void removeCustomer(Long id);

	void setUserProfilePhoto(byte[] bytesOfImage, MediaType mediaType);

	Customer updateCustomer(Long id, String firstName, String lastName);

	ProfilePhoto getUserProfilePhoto();
}

For our API binding to do any work, it needs to establish a Spring Social connection to the service provider. The connection may only be obtained after the token provider (our REST service from the oauth module) has given the client an accessToken. Spring Social handles all the work involved in obtaining the valid accessToken and connection, so let’s focus on writing our REST client using the RestTemplate.

We won’t rehash the entire implementation. For that, it might be easier to simply look at the source code online. Instead, let’s look at some of the salient excerpts and the things common to all of them.

Some of the code for our implementation - CrmTemplate - is shown in Listing JL-12. Note that we’re using a client side mirror API of types from the service implementation, not sharing. This is important because our service-side API might depend on classes that we don’t want polluting all users of our client library. In particular, this situation is particularly troublesome in Android where only a subset of the JDK classes are available (whitelisted).

The CrmTemplate implements our client API and is where we provide an HTTP client to the REST API.
package com.jl.crm.client;

import org.springframework.core.io.ByteArrayResource;
import org.springframework.hateoas.*;
import org.springframework.http.*;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.social.support.ClientHttpRequestFactorySelector;
import org.springframework.util.*;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.File;
import java.net.URI;
import java.util.*;

public class CrmTemplate extends AbstractOAuth2ApiBinding implements CrmOperations {

	private final File rootFile = new File(System.getProperty("java.io.tmpdir"));
	private URI apiBaseUri;

	public CrmTemplate(String accessToken, String apiUrl) {
		super(accessToken);
		try {
			this.apiBaseUri = new URI(apiUrl);
			setRequestFactory(
			  ClientHttpRequestFactorySelector.bufferRequests(getRestTemplate().getRequestFactory()));
		}
		catch (Exception e) {
			throw new RuntimeException("could not initialize the " + CrmTemplate.class.getName(), e);
		}
	}

	@Override
	public User currentUser() {
		ResponseEntity<UserResource> userResponse = this.getRestTemplate().getForEntity(uriFrom("/user"), UserResource.class);
		UserResource userResource = userResponse.getBody();
		return unwrapUser(userResource);
	}


	@Override
	public Customer createCustomer(String firstName, String lastName, Date signupDate) {

		Customer customer = new Customer(currentUser(), null, firstName, lastName, signupDate);

		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.setContentType(MediaType.APPLICATION_JSON);

		// build up a representation of the domain model and transmit it
		// as JSON no need to use the actual objects. this is simpler and more predictable.

		Map<String, Object> mapOfCutomerData = customerMap(customer);
		HttpEntity<Map<String, Object>> customerHttpEntity = new HttpEntity<Map<String, Object>>(mapOfCutomerData, httpHeaders);

		ResponseEntity<?> responseEntity = this.getRestTemplate().postForEntity(uriFrom("/customers"), customerHttpEntity, ResponseEntity.class);
		URI newLocation = responseEntity.getHeaders().getLocation();
		return customer(newLocation);
	}

	@Override
	public void removeCustomer(Long customer) {
		URI uri = uriFrom("/customers/" + Long.toString(customer));
		this.getRestTemplate().delete(uri);
	}

	@Override
	public void setUserProfilePhoto(byte[] bytesOfImage, final MediaType mediaType) {
		ByteArrayResource byteArrayResource = new ByteArrayResource(bytesOfImage) {
			@Override
			public String getFilename() {
				return new File(rootFile, "profile-image." + mediaType.getSubtype()).getAbsolutePath();
			}
		};
		MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
		parts.set("file", byteArrayResource);
		String photoUri = uriFrom("/users/" + currentUser().getDatabaseId() + "/photo").toString();
		ResponseEntity<?> responseEntity = this.getRestTemplate()
		    .postForEntity(photoUri, parts, ResponseEntity.class);
		HttpStatus.Series series = responseEntity.getStatusCode().series();
		if (!series.equals(HttpStatus.Series.SUCCESSFUL)){
			throw new RuntimeException("couldn't write the profile photo!");
		}
	}

	@Override
	public ProfilePhoto getUserProfilePhoto() {
		ResponseEntity<byte[]> profilePhotoData = this.getRestTemplate()
		   .getForEntity(uriFrom("/users/" + currentUser().getDatabaseId() + "/photo").toString(), byte[].class);
		MediaType mediaType = profilePhotoData.getHeaders().getContentType();
		return new ProfilePhoto(profilePhotoData.getBody(), mediaType);
	}

 	private Map<String, Object> customerMap(Customer customer) {
		Map<String, Object> mapOfUserData = null;
		if (customer.getUser() != null){
			mapOfUserData = new HashMap<String, Object>();
			mapOfUserData.put("id", customer.getUser().getDatabaseId());
		}
		Map<String, Object> mapOfCutomerData = new HashMap<String, Object>();
		mapOfCutomerData.put("firstName", customer.getFirstName());
		mapOfCutomerData.put("lastName", customer.getLastName());
		if (customer.getSignupDate() != null){
			// optional
			mapOfCutomerData.put("signupDate", customer.getSignupDate());
		}
		if (mapOfUserData != null){
			mapOfCutomerData.put("user", mapOfUserData);
		}

		return mapOfCutomerData;
	}

	private Customer customer(URI uri) {
		ResponseEntity<CustomerResource> customerResourceResponseEntity =
		   getRestTemplate().getForEntity(uri, CustomerResource.class);
		Resource<Customer> customerResource = customerResourceResponseEntity.getBody();
		return unwrapCustomer(customerResource);
	}

	private static User unwrapUser(Resource<User> tResource) {
		User user = tResource.getContent();
		user.setId(tResource.getId().getHref());
		return user;
	}

	// NB:
	// these are simply aliases to help avoid casts in our client code
	public static class CustomerList extends Resources<Resource<Customer>> {
	}

	public static class UserResource extends Resource<User> {
	}

	public static class CustomerResource extends Resource<Customer> {
	}

	...
}

There’s a lot in Listing JL-12. The client extends Spring Social’s AbstractOAuth2ApiBinding and gets a sweet restTemplate instance pre-configured to send the OAuth access token on each request (transparently) in the deal. One less thing for us to do! The createCustomer method demonstrates fairly typical use of the RestTemplate. To keep things simple, the class just sends Map<String,Object> instances to the service endpoint. These map-payloads get translated into acceptable JSON input by the RestTemplate which then sends them to the service.

The currentUser() method calls the REST endpoint /user, which is the one new REST endpoint that we added when we installed Spring Security. It simply returns the user that is currently authenticated. This service call will be useful later, when writing the Spring Social binding code.

Working with Binary Data

The implementation of the setUserProfilePhoto(byte[] bytesOfImage, final MediaType mediaType) method is interesting because it demonstrates how easy it is to do multipart file uploads. The getUserProfilePhoto() method does the opposite — it reads the profile photo. Here again, the RestTemplate makes short work of reading the bytes, the MediaType, and returning that back to the client.

Teaching Spring Social about our Client

We need to write a few classes to teach Spring Social about the API we’re writing a client for. We need to introduce Spring Social to our API Binding through the ingenious use of three interfaces: ConnectionFactory<CrmOperations> (shown in Listing JL-13), ServiceProvider<CrmOperations> (shown in Listing JL-14), and ApiAdapter<CrmOperations> (shown in Listing JL-15).

A ConnectionFactory<CrmOperations> implementation.
package com.jl.crm.client;

import org.springframework.social.connect.*;
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import org.springframework.social.oauth2.*;

public class CrmConnectionFactory extends OAuth2ConnectionFactory<CrmOperations> {
	public CrmConnectionFactory(CrmServiceProvider serviceProvider, CrmApiAdapter apiAdapter) {
		super("crm", serviceProvider, apiAdapter);
	}

	public CrmConnectionFactory( OAuth2ServiceProvider<CrmOperations> serviceProvider, ApiAdapter<CrmOperations> apiAdapter) {
		super("crm", serviceProvider, apiAdapter);
	}
}

The ServiceProvider implementation — CrmServiceProvider (shown in Listing JL-14) — is where the rubber meets the road: given an accessToken, this object needs to be able to hand back a concrete implementation of the service provider API interface (in our case, CrmTemplate is a concrete implementation of CrmOperations).

The getApi method is the critical part. It’s where the rubber meets the road and you get back a live API implementation.
package com.jl.crm.client;

import org.springframework.social.oauth2.*;

public class CrmServiceProvider extends AbstractOAuth2ServiceProvider<CrmOperations> {
	private String baseUrl;

	public CrmServiceProvider(
			                           String baseUrl,
			                           String clientId,
			                           String consumerSecret,
			                           String authorizeUrl,
			                           String accessTokenUrl) {
		super(new OAuth2Template(clientId, consumerSecret, authorizeUrl, accessTokenUrl));
		this.baseUrl = safeBaseUrl(baseUrl);
	}

	protected String safeBaseUrl(String baseUrl) {
		if (baseUrl.endsWith("/")){
			return "" + baseUrl.subSequence(0, baseUrl.lastIndexOf("/")  );
		}
		return baseUrl;
	}

	@Override
	public CrmOperations getApi(String accessToken) {
		return new CrmTemplate(accessToken, baseUrl);
	}
}

The ApiAdapter<CrmOperations> implementation — CrmApiAdapter (shown in Listing JL-15) — asks questions that most service providers can answer. Spring Social doesn’t try to provide an all-singing, all-dancing unified API for all service-providers (far from it!), but there is some overlap in terms of what operations most service-provider APIs can handle, including returning whether the connection itself is valid, returning the currently signed-in user’s profile, and updating the service-provider’s concept of a status, if it exists. On Facebook, for example, the `updateStatus method would translate to posting something to the user’s wall and on Twitter this would translate to tweeting.

The ApiAdapter implementation can answer commands like: "is the connection live?", "what’s the current user’s general profile information?", and "update my status!".
package com.jl.crm.client;

import org.springframework.social.connect.*;

public class CrmApiAdapter implements ApiAdapter<CrmOperations> {

	@Override
	public boolean test(CrmOperations customerServiceOperations) {
		return (null != customerServiceOperations.currentUser());
	}

	@Override
	public void setConnectionValues(CrmOperations customerServiceOperations, ConnectionValues values) {
		User profile = customerServiceOperations.currentUser();
		values.setProviderUserId(Long.toString(profile.getDatabaseId()));
		values.setDisplayName(profile.getUsername());
	}

	@Override
	public  UserProfile fetchUserProfile(CrmOperations customerServiceOperations) {
		User user = customerServiceOperations.currentUser();
		String name = user.getFirstName() + ' ' + user.getLastName();
		return new UserProfileBuilder()
				         .setName(name)
				         .setUsername(user.getUsername())
				         .setFirstName(user.getFirstName())
				         .setLastName(user.getLastName())
				         .build();
	}

	@Override
	public void updateStatus(CrmOperations customerServiceOperations, String message) {
		 System.out.println (String.format("calling updateStatus(CustomerServiceOperations customerServiceOperations, " +
		          "String message) with the status '%s', but this method is a no-op!", message));
	}
}

All Together Now..

We now have everyting we need to use our Spring Social binding in any application, be it a web application, a unit test, or an Android application. Listing JL-16 demonstrates the interaction with the API in a regular application. We won’t reprint the entire configuration class to run Spring Social standalone, but feel free to checkout the source code.

The program launches the system browser to the authorization URI (assumed to be running on localhost at port 8080). You must then authenticate and confirm the permission prompts. You’ll ultimately be sent back to the /crm/welcome.html page with an access_token in the URI. Extract that, and then paste it into the Swing input dialog that’ll be running.

Demonstrates the entire interaction with the API including the initial connection.
	@Inject private CrmConnectionFactory crmConnectionFactory;

	public void exampleCrmOperationsFlow () throws Exception {

		// build up the OAuth2Parameters
		String returnToUrl = environment.getProperty("sscrm.base-url") + "crm/welcome.html";
		OAuth2Template oAuth2Operations = crmConnectionFactory.getOAuthOperations();
		oAuth2Operations.setUseParametersForClientAuthentication(false);

		OAuth2Parameters parameters = new OAuth2Parameters();
		parameters.setScope("read,write");
		if (StringUtils.hasText(returnToUrl)){
			parameters.setRedirectUri(returnToUrl);
		}


		// figure out what the OAuth "authorize" endpoint should be and open it with
		// the system's default HTTP browser
		String authorizationUrl = oAuth2Operations.buildAuthenticateUrl(GrantType.IMPLICIT_GRANT, parameters);
		Desktop.getDesktop().browse(new URI(authorizationUrl));

		// the authorizationUrl above will have at a minimum, prompted an authenticated user to approve
		// certain permissions. After the approval, it will return with an `access_token` parameter
		// we must provide that `access_token` here.
		String i = JOptionPane.showInputDialog(null, "What's the 'access_token'?");
		String accessToken = i != null && !i.trim().equals("") ? i.trim() : null;


		// we have a live connection
		AccessGrant accessGrant = new AccessGrant(accessToken);
		connection = crmConnectionFactory.createConnection(accessGrant);

		UserProfile userProfile = connection.fetchUserProfile();

		String userId = userProfile.getUsername();
		Set<String> userIdSet = Sets.newHashSet(userId);

		// find out whether we've already connected before.
		// If not, save this connection information
		ConnectionRepository connectionRepository = this.usersConnectionRepository.createConnectionRepository(userId);
		if (usersConnectionRepository.findUserIdsConnectedTo(
		         crmConnectionFactory.getProviderId(), userIdSet).size() == 0){
			connectionRepository.addConnection(this.connection);
		}

		// Spring Social abstraction for service-provider's user profile data.
		UserProfile userProfile = connection.fetchUserProfile();

		// retrieve implementation of CrmTemplate
		CrmOperations customerServiceOperations = connection.getApi();

		// load the CRM User type (not Spring Social)
		User self = customerServiceOperations.currentUser();

		// insert a customer record
		Customer customer = customerServiceOperations.createCustomer("Chuck", "Norris", new java.util.Date());

		// read the user's profile photo
		ProfilePhoto profilePhoto = customerServiceOperations.getUserProfilePhoto();

		// write the profile photo to the desktop
		File photoOutputFile = new File(new File(SystemUtils.getUserHome(), "Desktop"), "profile.jpg");
		InputStream byteArrayInputStream = new ByteArrayInputStream(profilePhoto.getBytes());
		OutputStream outputStream = new FileOutputStream(photoOutputFile);
		IOUtils.copy(byteArrayInputStream, outputStream);
	}

Connected Android Applications with Spring Android

The Android classloader can be a pain in the neck because it whitelists a subset of a full JDK. Spring Android does not attempt to port all of Spring to Android. It doesn’t even attempt to port the container. Spring Android focuses on enabling delivering value for Android clients and - specifically for our application - Spring Android provides a port of the RestTemplate available in the core Spring framework.

Dodging Android Loader Issues

The Spring RestTemplate delegates conversion between a content type and well-known object types to the HttpMessageConverter hierarchy. The implementation that handles XML to object marshalling on Java SE delegates to JAXB. JAXB’s a safe choice with Java SE, but on Android it causes the application to be rejected because it uses classes that are not available there and thus can’t be loaded. Spring Android still supports XML REST request marshalling, but does so by delegating to a Simple XML-powered SimpleXmlHttpMessageConverter instead.

We’re using the Spring Android core types, but want to bring in our Spring Social binding, and that in turn depends on a few libraries from standard Spring. I’ve done a fair amount of work to exclude as much as possible so that I only have my dependencies and hard runtime dependencies on my CLASSPATH. Check out the android module’s pom.xml build file for details.

Dagger, a Dependency Injection Framework for Android

Spring Android does not provide a dependency injection solution on Android because - at the moment - there’s no really elegant way to do so and the jury’s still out on whether anything less is worth the effort. There are dependency injection solutions for Android. Some options support JSR 330-compliant dependency injection, including RoboGuice and Square’s Dagger.

Most dependency injection frameworks suffer from the same basic problem: Android manages the lifecycle of interesting components itself, and affords a framework no opportuity to provide instances that the framework manages instead. By the time you have a reference to one of them, they’re already constructed and alive. Dependency injection has to be done retroactively for these types of objects. For other, non-Android objects, the Dagger dependency injection container works as you’d expect given experience with JSR 330 and Spring’s Java configuration API. Our Android application, in the android Maven module, uses Dagger, Spring Social and Spring Android to drive a CRM Android client. We’ll focus specifically on the authorization screen here.

Dagger’s pretty good. It feels natural if you’re familiar with Spring Java configuration and JSR 330. Some of the integration code that you end up having to write is pretty ugly, but arguably better than the alternative. Integrating Dagger is pretty uninteresting, so I’ll instead defer you to the BaseActivity class, which integrates Dagger, and to our Android Application subclass, Crm, where Dagger is initialized.

Let’s look at our Dagger CrmModule configuration class in Listing JL-17. This is most simialar to a Spring Java configuration class. We use it to wire up the beans required to work with Spring Social. Once Spring Social’s up and running, we’ll drive an OAuth flow inside of an Android Activity subclass (AuthenticationActivity).

The @Module annotation marks this class as a class to be consulted for bean definitions, just as @Configuration does with Spring. Because of the performance constraints, scanning packages for beans is cumbersome on Android, and best avoided. Dagger uses an injects attribute on its modules to declare which classes beans might be injected into. The @Provides annotation tells Dagger that the method annotated provides bean definitions, just like @Bean in Spring. Unfortunately, in Dagger, things are not singleton by default, so we have to explicityly mark every singleton with the @Singleton annotation.

A caption for the code
package com.jl.crm.android;

import android.content.Context;
import android.location.LocationManager;
import android.view.LayoutInflater;
import com.jl.crm.android.activities.*;
import com.jl.crm.android.utils.AndroidUiThreadUtils;
import com.jl.crm.client.*;
import dagger.*;
import org.springframework.security.crypto.encrypt.*;
import org.springframework.social.connect.sqlite.SQLiteConnectionRepository;
import org.springframework.social.connect.sqlite.support.SQLiteConnectionRepositoryHelper;
import org.springframework.social.connect.support.ConnectionFactoryRegistry;

import javax.inject.Singleton;

import static android.content.Context.LOCATION_SERVICE;

@Module (injects = {UserHomeActivity.class, AuthenticationActivity.class, CustomerSearchActivity.class})
public class CrmModule {
	private Crm application;

	public CrmModule(Crm crm) {
		this.application = crm;
	}

	@Provides
	CrmOperations crmOperations(final SQLiteConnectionRepository sqLiteConnectionRepository) {
		try {
			CrmOperations ops = sqLiteConnectionRepository.getPrimaryConnection(CrmOperations.class).getApi();
			return AndroidUiThreadUtils.runOffUiThread(ops, CrmOperations.class);
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Provides
	@Singleton
	SQLiteConnectionRepository sqLiteConnectionRepository(SQLiteConnectionRepositoryHelper sqLiteConnectionRepositoryHelper,
			                                                       ConnectionFactoryRegistry connectionFactoryRegistry) {
		TextEncryptor textEncryptor = AndroidEncryptors.text("password", "5c0744940b5c369b");
		return new SQLiteConnectionRepository(sqLiteConnectionRepositoryHelper, connectionFactoryRegistry, textEncryptor);
	}

	@Provides
	@Singleton
	ConnectionFactoryRegistry connectionFactoryRegistry() {
		return new ConnectionFactoryRegistry();
	}

	@Provides
	@Singleton
	SQLiteConnectionRepositoryHelper repositoryHelper(@InjectAndroidApplicationContext Context context) {
		return new SQLiteConnectionRepositoryHelper(context);
	}

	@Provides
	@Singleton
	CrmApiAdapter apiAdapter() {
		return new CrmApiAdapter();
	}

	@Provides
	@Singleton
	CrmServiceProvider serviceProvider(@InjectAndroidApplicationContext Context c) {
		String baseUrl = c.getString(R.string.base_uri);
		String clientId = c.getString(R.string.oauth_client_id);
		String clientSecret = c.getString(R.string.oauth_client_secret);
		String accessTokenUri = fullUrl(baseUrl, c.getString(R.string.oauth_token_uri));
		String authorizeUri = fullUrl(baseUrl, c.getString(R.string.oauth_authorize_uri));
		return new CrmServiceProvider(baseUrl, clientId, clientSecret, authorizeUri, accessTokenUri);
	}

	@Provides
	@Singleton
	CrmConnectionFactory crmConnectionFactory(ConnectionFactoryRegistry connectionFactoryRegistry,
			                                           CrmServiceProvider crmServiceProvider,
			                                           CrmApiAdapter crmApiAdapter) {
		CrmConnectionFactory crmConnectionFactory = new CrmConnectionFactory(crmServiceProvider, crmApiAdapter);
		connectionFactoryRegistry.addConnectionFactory(crmConnectionFactory);
		return crmConnectionFactory;
	}

	@Provides
	@Singleton
	@InjectAndroidApplicationContext
	Context provideApplicationContext() {
		return this.application.getApplicationContext();
	}

	private String fullUrl(String baseUrl, String end) {
		String base = !baseUrl.endsWith("/") ? baseUrl + "/" : baseUrl;
		String newEnd = end.startsWith("/") ? end.substring(1) : end;
		return base + newEnd;
	}
}

Driving OAuth Authentication from within an Android Activity

With these pieces in place, we can write Activity subclasses that leverage beans we’ve configured in Dagger to support our OAuth flow.

Having an HTTP conversation using Spring Social’s OAuth support is slightly more difficult when one party can’t hear. Our client runs on a mobile device and exports no HTTP endpoints, so it’s something of a one-sided conversation as far as OAuth is concerned. We’ll drive the HTTP flow from inside of a WebView subclass that handles the OAuth dance for us, based on URIs that we build up using Spring Social. We can’t really receive redirect requests, but we can detect them as an event in the WebView subclass. Listing JL-18 shows the entire Android authorization screen, AuthenticationActivity.

A caption for the code
package com.jl.crm.android.activities;

import android.content.Intent;
import android.os.*;
import android.view.Window;
import com.jl.crm.android.R;
import com.jl.crm.android.widget.CrmOAuthFlowWebView;
import com.jl.crm.client.*;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.sqlite.SQLiteConnectionRepository;
import org.springframework.social.oauth2.*;
import org.springframework.util.*;

import javax.inject.Inject;
import java.util.List;

public class AuthenticationActivity extends BaseActivity {

	@Inject SQLiteConnectionRepository sqLiteConnectionRepository;

	@Inject CrmConnectionFactory connectionFactory;

	CrmOAuthFlowWebView webView;

	CrmOAuthFlowWebView.AccessTokenReceivedListener accessTokenReceivedListener = new CrmOAuthFlowWebView.AccessTokenReceivedListener() {

		@Override
		public void accessTokenReceived(final String accessToken) {

			AsyncTask<?, ?, Connection<CrmOperations>> asyncTask = new AsyncTask<Object, Object, Connection<CrmOperations>>() {
				@Override
				protected Connection<CrmOperations> doInBackground(Object... params) {
					AccessGrant accessGrant = new AccessGrant(accessToken);
					Connection<CrmOperations> crmOperationsConnection = connectionFactory.createConnection(accessGrant);
					sqLiteConnectionRepository.addConnection(crmOperationsConnection);
					runOnUiThread(connectionEstablishedRunnable);
					return crmOperationsConnection;
				}
			};
			try {
				asyncTask.execute(new Object[0]);
			}
			catch (Exception e) {
				throw new RuntimeException(e);
			}

		}
	};

	Runnable connectionEstablishedRunnable = new Runnable() {
		@Override
		public void run() {
			connectionEstablished();
		}
	};

	AsyncTask<?, ?, Connection<CrmOperations>> asyncTaskToLoadCrmOperationsConnection =
			  new AsyncTask<Object, Object, Connection<CrmOperations>>() {
				  @Override
				  protected Connection<CrmOperations> doInBackground(Object... params) {

					  Connection<CrmOperations> connection = sqLiteConnectionRepository.findPrimaryConnection(CrmOperations.class);
					  if (connection != null){
						  runOnUiThread(connectionEstablishedRunnable);
					  }
					  else {
						  runOnUiThread(new Runnable() {
							  @Override
							  public void run() {
								  webView.noAccessToken();
							  }
						  });
					  }
					  return null;
				  }
			  };

	@Override
	public void onStart() {
		super.onStart();
		asyncTaskToLoadCrmOperationsConnection.execute(new Object[]{});
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Window w = this.getWindow();
		w.requestFeature(Window.FEATURE_PROGRESS);
		w.setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_VISIBILITY_ON);

		this.webView = webView();

		setContentView(this.webView);
	}

	@SuppressWarnings("unused")
	protected void clearAllConnections() {
		MultiValueMap<String, Connection<?>> mvMapOfConnections = sqLiteConnectionRepository.findAllConnections();
		for (String k : mvMapOfConnections.keySet()) {
			List<Connection<?>> connectionList = mvMapOfConnections.get(k);
			for (Connection<?> c : connectionList) {
				sqLiteConnectionRepository.removeConnection(c.getKey());
			}
		}
	}

	protected void connectionEstablished() {
		Intent intent = new Intent(AuthenticationActivity.this, CustomerSearchActivity.class);
		startActivity(intent);
	}

	protected CrmOAuthFlowWebView webView() {
		String authenticateUri = buildAuthenticationUrl();
		String returnUri = getString(R.string.oauth_access_token_callback_uri);
		return new CrmOAuthFlowWebView(this, authenticateUri, returnUri, this.accessTokenReceivedListener);
	}

	protected String buildAuthenticationUrl() {
		OAuth2Operations oAuth2Operations = this.connectionFactory.getOAuthOperations();
		if (oAuth2Operations instanceof OAuth2Template){
			OAuth2Template oAuth2Template = (OAuth2Template) oAuth2Operations;
			oAuth2Template.setUseParametersForClientAuthentication(false);
		}
		String returnUri = getString(R.string.oauth_access_token_callback_uri);
		OAuth2Parameters oAuth2Parameters = new OAuth2Parameters();
		oAuth2Parameters.setScope("read,write");
		if (StringUtils.hasText(returnUri)){
			oAuth2Parameters.setRedirectUri(returnUri);
		}
		return oAuth2Operations.buildAuthenticateUrl(GrantType.IMPLICIT_GRANT, oAuth2Parameters);
	}
}

This code in Listing JL-18 is not easy to follow, at first blush, because there’s so much code related to running certain parts of the program on Android’s main thread, vs. Android’s UI thread. The program flow tends to get lost in the callback soup. The basic flow is as follows:

  • When the Activity starts, it installs a custom WebView subclass widget that handles the OAuth-dance for us. The Activity checks to see if there are already any Spring Social connections established in the local SQLite-powered ConnectionRepository implementation.

  • If there are existing connections, then it simply re-instantiates the connection. If there are no existing connections, it triggers the WebView subclass to begin the OAuth flow.

  • In either case, flow eventually lands at the connectionEstablished() method, where we launch a new Activity which may do what it pleases with the Dagger-injected CrmOperations crmOperations field which will be created per-request, and thus always reflect the latest connection.

Figure JL-6 shows the Android signin screen, delegating directly to the view rendered from the web application.

The Android CRM signin screen
Figure 6. The Android CRM signin screen

Once signed in, the user must approve the requested permissions, as shown in figure JL-7.

The Android CRM permission approval screen
Figure 7. The Android CRM permission approval screen

The whole thing culminates in connectionEstablished() which launches another Activity where we may inject a reference to our CrmOperations instance, secure in the knowledge that our connection is already setup and working.

Summary

We’ve introduced Spring’s powerful support for building REST services, employing Spring HATEOAS to elevate make our APIs easier for clients to use. We used Spring Data REST to remove the boilerplate, and we used Spring’s REST Shell to easily interact with our service. Finally, we’ve introduced security for both human and automatic clients using Spring Security and Spring Security OAuth, respectively. We built out a client using Spring Social that makes short work of consuming our OAuth-secured REST services. Then, thanks to the magic of Spring Android, we reused all that functionality in an Android application.

Where To Go From Here

There’s a lot more to Spring’s rich web application features. If you’re done reading this and are itching for more, then check out some of these other areas of Spring’s web application support.

  • Beyond REST, Spring MVC supports web-sockets for highly event-driven applications.

  • Spring MVC supports Servlet 3-powered asynchronous controllers.

  • The sister project Reactor provides a highly concurrent, asynchronous-IO focused architecture for building web applications. I’d definitely take a look at this!

  • The sister project Cujo.js aims to provide for JavaScript what Spring does for Java. The project provides several useful libraries. wire.js, for example, is used to wire together the application, on top of popular JavaScript module systems. rest.js provides a RestTemplate-like client (complete with support for HTTP Basic and OAuth-based connectivity!) for JavaScript clients. There’s a lot of great stuff here.

  • Longtime users of Spring will know that Spring supports multiple types of configuration, and that I’ve used all Java-based configuration in both of these installments. I think Java configuration is very readable, but there’s still more that we can do. The recently launched Spring Boot project takes that step for us by introducing opinionated setup, helping to remove all boilerplate interfering with your application, including the setup of a web server itself! Definitely watch this space!

About the Author

Josh Long is the Spring developer advocate at SpringSource, by Pivotal. Josh is the lead author on Apress’ Spring Recipes, 2nd Edition, O’Reilly’s Spring Roo, and a SpringSource open-source project committer and contributor. When he’s not hacking on code, he can be found at the local Java User Group or at the local coffee shop. Josh likes solutions that push the boundaries of the technologies that enable them. Continue the discussion on his personal blog, the SpringSource Blog and on Twitter (@starbuxman).