Building a Killer REST Client for Your REST+JSON API Les - - PowerPoint PPT Presentation

building a killer rest client for your rest json api
SMART_READER_LITE
LIVE PREVIEW

Building a Killer REST Client for Your REST+JSON API Les - - PowerPoint PPT Presentation

Building a Killer REST Client for Your REST+JSON API Les Hazlewood @lhazlewood Apache Shiro Project Chair CTO, Stormpath stormpath.com @lhazlewood |


slide-1
SLIDE 1

@lhazlewood ¡| ¡@goStormpath ¡

Building ¡a ¡Killer ¡REST ¡Client ¡ for ¡Your ¡REST+JSON ¡API ¡

Les ¡Hazlewood ¡@lhazlewood ¡ Apache ¡Shiro ¡Project ¡Chair ¡ CTO, ¡Stormpath ¡stormpath.com ¡

slide-2
SLIDE 2

@lhazlewood ¡| ¡@goStormpath ¡

¡.com ¡

  • User ¡Management ¡and ¡AuthenAcaAon ¡

API ¡

  • Security ¡for ¡your ¡applicaAons ¡
  • User ¡security ¡workflows ¡
  • Security ¡best ¡pracAces ¡
  • Developer ¡tools, ¡SDKs, ¡libraries ¡
slide-3
SLIDE 3

@lhazlewood ¡| ¡@goStormpath ¡

Overview ¡

  • Resources ¡
  • Public ¡/ ¡Private ¡API ¡
  • Proxy ¡Design ¡
  • AcAve ¡Record ¡
  • Fluent ¡API ¡
  • ConfiguraAon ¡
  • Caching ¡
  • AuthenAcaAon ¡
  • Pluggability ¡
  • Lessons ¡Learned ¡

¡ ¡

slide-4
SLIDE 4

@lhazlewood ¡| ¡@goStormpath ¡

HATEOAS ¡

  • Hypermedia ¡
  • As ¡
  • The ¡
  • Engine ¡
  • Of ¡
  • ApplicaAon ¡
  • State ¡

¡

slide-5
SLIDE 5

@lhazlewood ¡| ¡@goStormpath ¡

Resources ¡

slide-6
SLIDE 6

@lhazlewood ¡| ¡@goStormpath ¡

Resources ¡

  • Nouns, ¡not ¡verbs ¡
  • Coarse-­‑grained, ¡not ¡fine-­‑grained ¡
  • Support ¡many ¡use ¡cases ¡
  • Globally ¡unique ¡HREF ¡
slide-7
SLIDE 7

@lhazlewood ¡| ¡@goStormpath ¡

CollecHon ¡Resource ¡

  • Example: ¡ ¡

/applications

  • First ¡class ¡resource ¡w/ ¡own ¡properAes: ¡
  • offset
  • limit
  • items
  • first, next, previous, last
  • etc ¡
  • items ¡contains ¡instance ¡resources ¡
slide-8
SLIDE 8

@lhazlewood ¡| ¡@goStormpath ¡

Instance ¡Resource ¡

  • Example: ¡

/applications/8sZxUoExA30mP74

  • Child ¡of ¡a ¡collecAon ¡
  • RUD ¡(no ¡Create ¡-­‑ ¡done ¡via ¡parent ¡collecAon) ¡
slide-9
SLIDE 9

@lhazlewood ¡| ¡@goStormpath ¡

TranslaHng ¡to ¡Code ¡

slide-10
SLIDE 10

@lhazlewood ¡| ¡@goStormpath ¡

Resource ¡

public interface Resource { String getHref(); }

slide-11
SLIDE 11

@lhazlewood ¡| ¡@goStormpath ¡

Instance ¡Resource ¡

public interface Application extends Resource, Saveable, Deleteable { ... }

  • public interface Saveable {

void save(); }

  • public interface Deletable {

void delete(); }

slide-12
SLIDE 12

@lhazlewood ¡| ¡@goStormpath ¡

CollecHon ¡Resource ¡

public interface CollectionResource<T extends Resource> extends Resource, Iterable<T> {

  • int getOffset();
  • int getLimit();
  • }
slide-13
SLIDE 13

@lhazlewood ¡| ¡@goStormpath ¡

Example: ¡ApplicaHonList ¡

public interface ApplicationList extends CollectionResource<Application> { }

slide-14
SLIDE 14

@lhazlewood ¡| ¡@goStormpath ¡

Design! ¡

slide-15
SLIDE 15

@lhazlewood ¡| ¡@goStormpath ¡

EncapsulaHon ¡

  • Public ¡API ¡
  • Internal/Private ¡ImplementaAons ¡
  • Extensions ¡
  • Allows ¡for ¡change ¡w/ ¡minimal ¡impact ¡

hZp://semver.org ¡

slide-16
SLIDE 16

@lhazlewood ¡| ¡@goStormpath ¡

EncapsulaHon ¡in ¡pracHce ¡

project-root/ |- api/ | |- src/main/java | |- impl/ | |- src/main/java | |- extendsions/ | |- src/main/java | |- pom.xml

slide-17
SLIDE 17

@lhazlewood ¡| ¡@goStormpath ¡

Public ¡API ¡

slide-18
SLIDE 18

@lhazlewood ¡| ¡@goStormpath ¡

Public ¡API ¡

  • All ¡interfaces ¡
  • Helper ¡classes ¡with ¡staAc ¡methods ¡
  • Builder ¡interfaces ¡for ¡configuraAon ¡

¡

  • NO ¡IMPLEMENTATIONS ¡EXPOSED ¡
slide-19
SLIDE 19

@lhazlewood ¡| ¡@goStormpath ¡

Example ¡interfaces ¡

  • Client ¡
  • ClientBuilder ¡
  • ApplicaAon ¡
  • Directory ¡
  • Account ¡
  • Group ¡
  • etc ¡
slide-20
SLIDE 20

@lhazlewood ¡| ¡@goStormpath ¡

Classes ¡with ¡staHc ¡helper ¡methods ¡

Client client = Clients.builder() ... .build();

  • Create ¡mulAple ¡helper ¡classes ¡

separaAon ¡of ¡concerns ¡

slide-21
SLIDE 21

@lhazlewood ¡| ¡@goStormpath ¡

Builder ¡interfaces ¡for ¡configuraHon ¡

Client client = Clients.builder().setApiKey( ApiKeys.builder().setFileLocation( “$HOME/.stormpath/apiKey.properties”) .build()) .build();

  • Clients.builder() à ClientBuilder

ApiKeys.builder() à ApiKeyBuilder

  • Single ¡Responsibility ¡Principle! ¡ ¡
slide-22
SLIDE 22

@lhazlewood ¡| ¡@goStormpath ¡

Private ¡API ¡

  • ImplementaAons ¡+ ¡SPI ¡interfaces ¡
  • Builder ¡implementaAons ¡
  • ImplementaAon ¡Plugins ¡

¡

slide-23
SLIDE 23

@lhazlewood ¡| ¡@goStormpath ¡

Resource ¡ImplementaHons ¡

  • Create ¡a ¡base ¡AbstractResource ¡class: ¡
  • Map ¡manipulaAon ¡methods ¡
  • Dirty ¡checking ¡
  • Reference ¡to ¡DataStore ¡
  • Lazy ¡Loading ¡
  • Locks ¡for ¡concurrent ¡access ¡
  • Create ¡abstract ¡InstanceResource ¡and ¡CollecAonResource ¡

implementaAons ¡

  • Extend ¡from ¡InstanceResource ¡or ¡CollecAonResource ¡
slide-24
SLIDE 24

@lhazlewood ¡| ¡@goStormpath ¡

Resource ¡ImplementaHons ¡

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; } }

slide-25
SLIDE 25

@lhazlewood ¡| ¡@goStormpath ¡

Usage ¡Paradigm ¡

slide-26
SLIDE 26

@lhazlewood ¡| ¡@goStormpath ¡

Account ¡JSON ¡Resource ¡

{ “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: { “href”: “https://api.stormpath.com/v1/directories/ g4h5i6” } }

slide-27
SLIDE 27

@lhazlewood ¡| ¡@goStormpath ¡

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”));

slide-28
SLIDE 28

@lhazlewood ¡| ¡@goStormpath ¡

Naïve ¡Design ¡(typesafe ¡language) ¡

  • Results ¡in ¡*huge* ¡amount ¡of ¡Boilerplate ¡code ¡
  • Not ¡good ¡
  • Find ¡another ¡way ¡
slide-29
SLIDE 29

@lhazlewood ¡| ¡@goStormpath ¡

Proxy ¡PaUern ¡

String href = “https://api.stormpath.com/v1/....”; Account account = client.getAccount(href);

  • Directory directory = account.getDirectory();
  • System.out.println(directory.getName());
slide-30
SLIDE 30

@lhazlewood ¡| ¡@goStormpath ¡

Proxy ¡PaUern ¡

slide-31
SLIDE 31

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Design ¡

slide-32
SLIDE 32

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ .save() ¡

slide-33
SLIDE 33

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ .save() ¡

DataStore ¡

slide-34
SLIDE 34

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ .save() ¡

MapMarshaller ¡ ¡ ¡ ¡JSON ¡<-­‑-­‑> ¡Map ¡

DataStore ¡

slide-35
SLIDE 35

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ .save() ¡

ResourceFactory ¡ ¡ ¡ ¡Map ¡à ¡Resource ¡ MapMarshaller ¡ ¡ ¡ ¡JSON ¡<-­‑-­‑> ¡Map ¡

DataStore ¡

slide-36
SLIDE 36

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ .save() ¡

ResourceFactory ¡ ¡ ¡ ¡Map ¡à ¡Resource ¡ MapMarshaller ¡ ¡ ¡ ¡JSON ¡<-­‑-­‑> ¡Map ¡ Cache ¡ Manager ¡

DataStore ¡

slide-37
SLIDE 37

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ .save() ¡ RequestExecutor ¡

ResourceFactory ¡ ¡ ¡ ¡Map ¡à ¡Resource ¡ MapMarshaller ¡ ¡ ¡ ¡JSON ¡<-­‑-­‑> ¡Map ¡ Cache ¡ Manager ¡

DataStore ¡

slide-38
SLIDE 38

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ .save() ¡ RequestExecutor ¡

ResourceFactory ¡ ¡ ¡ ¡Map ¡à ¡Resource ¡

AuthenAcaAon Strategy ¡

MapMarshaller ¡ ¡ ¡ ¡JSON ¡<-­‑-­‑> ¡Map ¡ Cache ¡ Manager ¡

DataStore ¡

Request ¡ AuthenAcator ¡

slide-39
SLIDE 39

@lhazlewood ¡| ¡@goStormpath ¡

Component ¡Architecture ¡

account ¡ API ¡Server ¡ .save() ¡ RequestExecutor ¡

ResourceFactory ¡ ¡ ¡ ¡Map ¡à ¡Resource ¡

AuthenAcaAon Strategy ¡

MapMarshaller ¡ ¡ ¡ ¡JSON ¡<-­‑-­‑> ¡Map ¡ Cache ¡ Manager ¡

DataStore ¡

Request ¡ AuthenAcator ¡

slide-40
SLIDE 40

@lhazlewood ¡| ¡@goStormpath ¡

Caching ¡

slide-41
SLIDE 41

@lhazlewood ¡| ¡@goStormpath ¡

Caching ¡

public interface CacheManager { Cache getCache(String regionName); }

  • public interface Cache {

long getTtl(); long getTti(); ... Map<String,Object> get(String href); ... other map methods ... }

slide-42
SLIDE 42

@lhazlewood ¡| ¡@goStormpath ¡

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) ...

slide-43
SLIDE 43

@lhazlewood ¡| ¡@goStormpath ¡

Queries ¡

slide-44
SLIDE 44

@lhazlewood ¡| ¡@goStormpath ¡

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? ¡
slide-45
SLIDE 45

@lhazlewood ¡| ¡@goStormpath ¡

Queries ¡

Use ¡a ¡Fluent ¡API!

slide-46
SLIDE 46

@lhazlewood ¡| ¡@goStormpath ¡

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

slide-47
SLIDE 47

@lhazlewood ¡| ¡@goStormpath ¡

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

slide-48
SLIDE 48

@lhazlewood ¡| ¡@goStormpath ¡

AuthenHcaHon ¡

slide-49
SLIDE 49

@lhazlewood ¡| ¡@goStormpath ¡

AuthenHcaHon ¡

  • Favor ¡a ¡digest ¡algorithm ¡over ¡HTTP ¡Basic ¡
  • Prevents ¡Man-­‑in-­‑the-­‑Middle ¡aZacks ¡(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

slide-50
SLIDE 50

@lhazlewood ¡| ¡@goStormpath ¡

AuthenHcaHon ¡

  • 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.

slide-51
SLIDE 51

@lhazlewood ¡| ¡@goStormpath ¡

Plugins ¡

slide-52
SLIDE 52

@lhazlewood ¡| ¡@goStormpath ¡

Plugins ¡

  • Plugins ¡or ¡Extensions ¡module ¡
  • One ¡sub-­‑module ¡per ¡plugin ¡
  • Keep ¡dependencies ¡to ¡a ¡minimum ¡

¡ extensions/ |- httpclient |- src/main/java

slide-53
SLIDE 53

@lhazlewood ¡| ¡@goStormpath ¡

Lessons ¡Learned ¡

slide-54
SLIDE 54

@lhazlewood ¡| ¡@goStormpath ¡

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. ¡

slide-55
SLIDE 55

@lhazlewood ¡| ¡@goStormpath ¡

Lessons ¡Learned: ¡Async, ¡Async! ¡

  • Async ¡clients ¡can ¡be ¡used ¡synchronously ¡

easily, ¡but ¡not ¡the ¡other ¡way ¡around ¡ ¡

  • Vert.x, ¡NeZy, ¡Scala, ¡Clojure, ¡etc. ¡all ¡require ¡

async ¡– ¡hard ¡to ¡use ¡your ¡SDK ¡otherwise ¡

  • NeZy ¡has ¡a ¡*great* ¡Async ¡HTTP ¡Client ¡that ¡

can ¡be ¡the ¡base ¡of ¡your ¡client ¡SDK ¡

slide-56
SLIDE 56

@lhazlewood ¡| ¡@goStormpath ¡

Lessons ¡Learned: ¡Async! ¡

account.req().groups().where()... .execute(new ResultListener<GroupList>() {

  • nSuccess(GroupList groups){...}
  • nFailure(ResourceException ex) {...}

}

  • account.req() -> RequestBuilder

execute -> async ¡call ¡w/ ¡promise ¡callback ¡

slide-57
SLIDE 57

@lhazlewood ¡| ¡@goStormpath ¡

Lessons ¡Learned: ¡Sync ¡

Sync ¡is ¡sAll ¡easy: ¡

  • account.getGroups() just ¡delegates ¡to: ¡
  • account.req().groups()... .get();
slide-58
SLIDE 58

@lhazlewood ¡| ¡@goStormpath ¡

Code ¡

$ git clone https://github.com/stormpath/ stormpath-sdk-java.git $ cd stormpath-sdk-java $ mvn install

slide-59
SLIDE 59

@lhazlewood ¡| ¡@goStormpath ¡

Thank ¡You! ¡

  • les@stormpath.com ¡
  • TwiZer: ¡@lhazlewood ¡
  • hZp://www.stormpath.com ¡