Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Build A Killer Client For Your REST+JSON API

2,930 views

Published on

REST+JSON APIs are great - but you still need to communicate with them from your code. Wouldn't you prefer to interact with clean and intuitive Java objects instead of messing with HTTP requests, HTTP status codes and JSON parsing? Wouldn't you prefer to work with type-safe objects specific to your API?

In this presentation, Les Hazlewood - Stormpath CTO and Apache Shiro PMC Chair - will share all of the golden nuggets learned while designing, implementing and supporting multiple clients purpose-built for a real-world REST+JSON API.

Further reading: http://www.stormpath.com/blog

Stormpath is a user management and authentication service for developers. By offloading user management and authentication to Stormpath, developers can bring applications to market faster, reduce development costs, and protect their users. Easy and secure, the flexible cloud service can manage millions of users with a scalable pricing model.

Published in: Software, Technology, Education

Build A Killer Client For Your REST+JSON API

  1. 1. Building a Killer REST Client for Your REST+JSON API Les Hazlewood @lhazlewood Apache Shiro Project Chair CTO, Stormpath stormpath.com
  2. 2. .com • User Management and Authentication API • Security for your applications • User security workflows • Security best practices • Developer tools, SDKs, libraries
  3. 3. Overview • Resources • Public / Private API • Proxy Design • Active Record • Fluent API • Configuration • Caching • Authentication • Pluggability • Lessons Learned
  4. 4. HATEOAS • Hypermedia • As • The • Engine • Of • Application • State Learn more at Stormpath.com
  5. 5. Resources Learn more at Stormpath.com
  6. 6. Resources • Nouns, not verbs • Coarse-grained, not fine-grained • Support many use cases • Globally unique HREF Learn more at Stormpath.com
  7. 7. Collection Resource • Example: /applications • First class resource w/ own properties: • offset • limit • items • first, next, previous, last • etc • items contains instance resources Learn more at Stormpath.com
  8. 8. Instance Resource • Example: /applications/8sZxUoExA30mP74 • Child of a collection • RUD (no Create - done via parent collection) Learn more at Stormpath.com
  9. 9. Translating to Code Learn more at Stormpath.com
  10. 10. Resource public interface Resource { String getHref(); } Learn more at Stormpath.com
  11. 11. Instance Resource public interface Application extends Resource, Saveable, Deleteable { ... } public interface Saveable { void save(); } public interface Deletable { void delete(); } Learn more at Stormpath.com
  12. 12. Collection Resource public interface CollectionResource<T extends Resource> extends Resource, Iterable<T> { int getOffset(); int getLimit(); } Learn more at Stormpath.com
  13. 13. Example: ApplicationList public interface ApplicationList extends CollectionResource<Application> { } Learn more at Stormpath.com
  14. 14. Design! Learn more at Stormpath.com
  15. 15. Encapsulation • Public API • Internal/Private Implementations • Extensions • Allows for change w/ minimal impact http://semver.org Learn more at Stormpath.com
  16. 16. Encapsulation in practice project-root/ |- api/ | |- src/main/java | |- impl/ | |- src/main/java | |- extendsions/ | |- src/main/java | |- pom.xml Learn more at Stormpath.com
  17. 17. Public API Learn more at Stormpath.com
  18. 18. Public API • All interfaces • Helper classes with static methods • Builder interfaces for configuration • NO IMPLEMENTATIONS EXPOSED Learn more at Stormpath.com
  19. 19. Example interfaces • Client • ClientBuilder • Application • Directory • Account • Group • etc Learn more at Stormpath.com
  20. 20. Classes with static helper methods Client client = Clients.builder() ... .build(); • Create multiple helper classes separation of concerns Learn more at Stormpath.com
  21. 21. Builder interfaces for configuration Client client = Clients.builder().setApiKey( ApiKeys.builder().setFileLocation( “$HOME/.stormpath/apiKey.properties”) .build()) .build(); Clients.builder()  ClientBuilder ApiKeys.builder()  ApiKeyBuilder Single Responsibility Principle! Learn more at Stormpath.com
  22. 22. Private API • Implementations + SPI interfaces • Builder implementations • Implementation Plugins Learn more at Stormpath.com
  23. 23. Resource Implementations • Create a base AbstractResource class: • Map manipulation methods • Dirty checking • Reference to DataStore • Lazy Loading • Locks for concurrent access • Create abstract InstanceResource and CollectionResource implementations • Extend from InstanceResource or CollectionResource Learn more at Stormpath.com
  24. 24. Resource Implementations public class DefaultAccount extends InstanceResource implements Account { @Override public String getName() { return (String)getProperty(“name”); } @Override public Account setName(String name) { setProperty(“name”, name); return this; } } Learn more at Stormpath.com
  25. 25. Usage Paradigm Learn more at Stormpath.com
  26. 26. Account JSON Resource { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: { “href”: “https://api.stormpath.com/v1/directories/g4h5i6” } } Learn more at Stormpath.com
  27. 27. Naïve Design (typesafe language) //get account String href = “https://api.stormpath.com/v1/....”; Map<String,Object> account = client.getResource(href); //get account‟s parent directory via link: Map<String,Object> dirLink = account.getDirectory(); String dirHref = (String)dirLink.get(“href”); Map<String,Object> directory = client.getResource(dirHref); System.out.println(directory.get(“name”)); Learn more at Stormpath.com
  28. 28. Naïve Design (typesafe language) • Results in *huge* amount of Boilerplate code • Not good • Find another way Learn more at Stormpath.com
  29. 29. Proxy Pattern String href = “https://api.stormpath.com/v1/....”; Account account = client.getAccount(href); Directory directory = account.getDirectory(); System.out.println(directory.getName()); Learn more at Stormpath.com
  30. 30. Proxy Pattern Learn more at Stormpath.com
  31. 31. Component Design Learn more at Stormpath.com
  32. 32. Component Architecture account .save() Learn more at Stormpath.com
  33. 33. Component Architecture account .save() DataStore Learn more at Stormpath.com
  34. 34. Component Architecture account .save() MapMarshaller JSON <--> Map DataStore Learn more at Stormpath.com
  35. 35. Component Architecture account .save() ResourceFactory Map  Resource MapMarshaller JSON <--> Map DataStore Learn more at Stormpath.com
  36. 36. Component Architecture account .save() ResourceFactory Map  Resource MapMarshaller JSON <--> Map Cache Manager DataStore Learn more at Stormpath.com
  37. 37. Component Architecture account .save() RequestExecutor ResourceFactory Map  Resource MapMarshaller JSON <--> Map Cache Manager DataStore Learn more at Stormpath.com
  38. 38. Component Architecture account .save() RequestExecutor ResourceFactory Map  Resource Authentication Strategy MapMarshaller JSON <--> Map Cache Manager DataStore Request Authenticator Learn more at Stormpath.com
  39. 39. Component Architecture account API Server .save() RequestExecutor ResourceFactory Map  Resource Authentication Strategy MapMarshaller JSON <--> Map Cache Manager DataStore Request Authenticator Learn more at Stormpath.com
  40. 40. Caching Learn more at Stormpath.com
  41. 41. Caching public interface CacheManager { Cache getCache(String regionName); } public interface Cache { long getTtl(); long getTti(); ... Map<String,Object> get(String href); ... other map methods ... } Learn more at Stormpath.com
  42. 42. Caching Account account = client.getAccount(href); //DataStore: Cache cache = cacheManager.getCache(“accounts”); Map<String,Object> accountProperties = cache.get(href); if (accountProps != null) { return resourceFactory.create(Account.class, props); } //otherwise, query the server: requestExeuctor.get(href) ... Learn more at Stormpath.com
  43. 43. Queries Learn more at Stormpath.com
  44. 44. Queries GroupList groups = account.getGroups(); //results in a request to: //https://api.stormpath.com/v1/accounts/a1b2c3/groups • What about query parameters? • How do we make this type safe? Learn more at Stormpath.com
  45. 45. Queries Use a Fluent API! Learn more at Stormpath.com
  46. 46. Queries GroupList groups = account.getGroups(Groups.where() .name().startsWith(“foo”) .description().contains(“test”) .orderBy(“name”).desc() .limitTo(100) ); //results in a request to: https://api.stormpath.com/v1/accounts/a1b2c3/groups? name=foo*&description=*test*&orderBy=name%20desc&limit=100 Learn more at Stormpath.com
  47. 47. Queries Also support simple map for dynamic languages, for example, groovy: def groups = account.getGroups([name: „foo*‟, description:‟*test*‟, orderBy:‟name desc‟, limit: 100]); //results in a request to: https://api.stormpath.com/v1/accounts/a1b2c3/groups? name=foo*&description=*test*&orderBy=name%20desc&limit=100 Learn more at Stormpath.com
  48. 48. Authentication Learn more at Stormpath.com
  49. 49. Authentication • Favor a digest algorithm over HTTP Basic • Prevents Man-in-the-Middle attacks (SSL won’t guarantee this!) • Also support Basic for environments that require it (Dammit Google!) • ONLY use Basic over SSL • Represent this as an AuthenticationScheme to your ClientBuilder Learn more at Stormpath.com
  50. 50. Authentication • AuthenticationScheme.SAUTHC1 • AuthenticationScheme.BASIC • AuthenticationScheme.OAUTH10a • ... etc ... Client client = Clients.builder() ... //defaults to SAUTHC1 .setAuthenticationScheme(BASIC) .build(); Client uses a Sauthc1RequestAuthenticator or BasicRequestAuthenticator or OAuth10aRequestAuthenticator, etc. Learn more at Stormpath.com
  51. 51. Plugins Learn more at Stormpath.com
  52. 52. Plugins • Plugins or Extensions module • One sub-module per plugin • Keep dependencies to a minimum extensions/ |- httpclient |- src/main/java Learn more at Stormpath.com
  53. 53. Lessons Learned Learn more at Stormpath.com
  54. 54. Lessons Learned • Recursive caching if you support resource expansion • Dirty checking logic is not too hard, but it does add complexity. Start off without it. Learn more at Stormpath.com
  55. 55. Lessons Learned: Async, Async! • Async clients can be used synchronously easily, but not the other way around • Vert.x, Netty, Scala, Clojure, etc. all require async – hard to use your SDK otherwise • Netty has a *great* Async HTTP Client that can be the base of your client SDK Learn more at Stormpath.com
  56. 56. Lessons Learned: Async! account.req().groups().where()... .execute(new ResultListener<GroupList>() { onSuccess(GroupList groups){...} onFailure(ResourceException ex) {...} } account.req() -> RequestBuilder execute -> async call w/ promise callback Learn more at Stormpath.com
  57. 57. Lessons Learned: Sync Sync is still easy: account.getGroups() just delegates to: account.req().groups()... .get(); Learn more at Stormpath.com
  58. 58. $ git clone https://github.com/stormpath/stormpath-sdk- java.git $ cd stormpath-sdk-java $ mvn install Code Learn more at Stormpath.com
  59. 59. Thank You! • les@stormpath.com • Twitter: @lhazlewood • http://www.stormpath.com Learn more at Stormpath.com

×