YOU ARE DOWNLOADING DOCUMENT

Please tick the box to continue:

Transcript
Page 1: Build A Killer Client For Your REST+JSON API

Building a Killer REST Clientfor Your REST+JSON API

Les Hazlewood @lhazlewoodApache Shiro Project Chair

CTO, Stormpath stormpath.com

Page 2: Build A Killer Client For Your REST+JSON API

.com• User Management and Authentication

API• Security for your applications• User security workflows• Security best practices• Developer tools, SDKs, libraries

Page 3: Build A Killer Client For Your REST+JSON API

Overview• Resources• Public / Private API• Proxy Design• Active Record• Fluent API• Configuration• Caching• Authentication• Pluggability• Lessons Learned

Page 4: Build A Killer Client For Your REST+JSON API

HATEOAS

• Hypermedia

• As

• The

• Engine

• Of

• Application

• State

Learn more at Stormpath.com

Page 5: Build A Killer Client For Your REST+JSON API

Resources

Learn more at Stormpath.com

Page 6: Build A Killer Client For Your REST+JSON API

Resources

• Nouns, not verbs• Coarse-grained, not fine-grained• Support many use cases• Globally unique HREF

Learn more at Stormpath.com

Page 7: Build A Killer Client For Your REST+JSON API

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

Page 8: Build A Killer Client For Your REST+JSON API

Instance Resource

• Example:/applications/8sZxUoExA30mP74

• Child of a collection• RUD (no Create - done via parent collection)

Learn more at Stormpath.com

Page 9: Build A Killer Client For Your REST+JSON API

Translating to Code

Learn more at Stormpath.com

Page 10: Build A Killer Client For Your REST+JSON API

Resource

public interface Resource { String getHref();}

Learn more at Stormpath.com

Page 11: Build A Killer Client For Your REST+JSON API

Instance Resourcepublic interface Application extends Resource, Saveable, Deleteable { ...}

public interface Saveable { void save();}

public interface Deletable { void delete();}

Learn more at Stormpath.com

Page 12: Build A Killer Client For Your REST+JSON API

Collection Resourcepublic interface CollectionResource<T extends Resource> extends Resource, Iterable<T> {

int getOffset();

int getLimit();

}

Learn more at Stormpath.com

Page 13: Build A Killer Client For Your REST+JSON API

Example: ApplicationListpublic interface ApplicationList extends CollectionResource<Application> { }

Learn more at Stormpath.com

Page 14: Build A Killer Client For Your REST+JSON API

Design!

Learn more at Stormpath.com

Page 15: Build A Killer Client For Your REST+JSON API

Encapsulation

• Public API• Internal/Private Implementations• Extensions

• Allows for change w/ minimal impacthttp://semver.org

Learn more at Stormpath.com

Page 16: Build A Killer Client For Your REST+JSON API

Encapsulation in practiceproject-root/|- api/| |- src/main/java||- impl/| |- src/main/java||- extendsions/| |- src/main/java||- pom.xml

Learn more at Stormpath.com

Page 17: Build A Killer Client For Your REST+JSON API

Public API

Learn more at Stormpath.com

Page 18: Build A Killer Client For Your REST+JSON API

Public API

• All interfaces• Helper classes with static methods• Builder interfaces for configuration

• NO IMPLEMENTATIONS EXPOSED

Learn more at Stormpath.com

Page 19: Build A Killer Client For Your REST+JSON API

Example interfaces

• Client• ClientBuilder• Application• Directory• Account• Group• etc

Learn more at Stormpath.com

Page 20: Build A Killer Client For Your REST+JSON API

Classes with static helper methodsClient client = Clients.builder() ... .build();

• Create multiple helper classesseparation of concerns

Learn more at Stormpath.com

Page 21: Build A Killer Client For Your REST+JSON API

Builder interfaces for configuration

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

Clients.builder() ClientBuilderApiKeys.builder() ApiKeyBuilder

Single Responsibility Principle!

Learn more at Stormpath.com

Page 22: Build A Killer Client For Your REST+JSON API

Private API

• Implementations + SPI interfaces• Builder implementations• Implementation Plugins

Learn more at Stormpath.com

Page 23: Build A Killer Client For Your REST+JSON API

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

Page 24: Build A Killer Client For Your REST+JSON API

Resource Implementationspublic 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

Page 25: Build A Killer Client For Your REST+JSON API

Usage Paradigm

Learn more at Stormpath.com

Page 26: Build A Killer Client For Your REST+JSON API

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

Page 27: Build A Killer Client For Your REST+JSON API

Naïve Design (typesafe language)//get accountString 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

Page 28: Build A Killer Client For Your REST+JSON API

Naïve Design (typesafe language)

• Results in *huge* amount of Boilerplate code• Not good• Find another way

Learn more at Stormpath.com

Page 29: Build A Killer Client For Your REST+JSON API

Proxy PatternString 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

Page 30: Build A Killer Client For Your REST+JSON API

Proxy Pattern

Learn more at Stormpath.com

Page 31: Build A Killer Client For Your REST+JSON API

Component Design

Learn more at Stormpath.com

Page 32: Build A Killer Client For Your REST+JSON API

Component Architectureaccount .save()

Learn more at Stormpath.com

Page 33: Build A Killer Client For Your REST+JSON API

Component Architectureaccount .save()

DataStore

Learn more at Stormpath.com

Page 34: Build A Killer Client For Your REST+JSON API

Component Architectureaccount .save()

MapMarshaller JSON <--> Map

DataStore

Learn more at Stormpath.com

Page 35: Build A Killer Client For Your REST+JSON API

Component Architectureaccount .save()

ResourceFactory Map Resource

MapMarshaller JSON <--> Map

DataStore

Learn more at Stormpath.com

Page 36: Build A Killer Client For Your REST+JSON API

Component Architectureaccount .save()

ResourceFactory Map Resource

MapMarshaller JSON <--> Map

CacheManager

DataStore

Learn more at Stormpath.com

Page 37: Build A Killer Client For Your REST+JSON API

Component Architectureaccount .save()

RequestExecutor

ResourceFactory Map Resource

MapMarshaller JSON <--> Map

CacheManager

DataStore

Learn more at Stormpath.com

Page 38: Build A Killer Client For Your REST+JSON API

Component Architectureaccount .save()

RequestExecutor

ResourceFactory Map Resource

AuthenticationStrategy

MapMarshaller JSON <--> Map

CacheManager

DataStore

RequestAuthenticator

Learn more at Stormpath.com

Page 39: Build A Killer Client For Your REST+JSON API

Component Architectureaccount

API Server

.save()

RequestExecutor

ResourceFactory Map Resource

AuthenticationStrategy

MapMarshaller JSON <--> Map

CacheManager

DataStore

RequestAuthenticator

Learn more at Stormpath.com

Page 40: Build A Killer Client For Your REST+JSON API

Caching

Learn more at Stormpath.com

Page 41: Build A Killer Client For Your REST+JSON API

Cachingpublic 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

Page 42: Build A Killer Client For Your REST+JSON API

CachingAccount 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

Page 43: Build A Killer Client For Your REST+JSON API

Queries

Learn more at Stormpath.com

Page 44: Build A Killer Client For Your REST+JSON API

QueriesGroupList 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

Page 45: Build A Killer Client For Your REST+JSON API

Queries

Use a Fluent API!

Learn more at Stormpath.com

Page 46: Build A Killer Client For Your REST+JSON API

QueriesGroupList 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

Page 47: Build A Killer Client For Your REST+JSON API

QueriesAlso 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

Page 48: Build A Killer Client For Your REST+JSON API

Authentication

Learn more at Stormpath.com

Page 49: Build A Killer Client For Your REST+JSON API

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

Page 50: Build A Killer Client For Your REST+JSON API

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

Page 51: Build A Killer Client For Your REST+JSON API

Plugins

Learn more at Stormpath.com

Page 52: Build A Killer Client For Your REST+JSON API

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

Page 53: Build A Killer Client For Your REST+JSON API

Lessons Learned

Learn more at Stormpath.com

Page 54: Build A Killer Client For Your REST+JSON API

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

Page 55: Build A Killer Client For Your REST+JSON API

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

Page 56: Build A Killer Client For Your REST+JSON API

Lessons Learned: Async!account.req().groups().where()....execute(new ResultListener<GroupList>() { onSuccess(GroupList groups){...} onFailure(ResourceException ex) {...}}

account.req() -> RequestBuilderexecute -> async call w/ promise callback

Learn more at Stormpath.com

Page 57: Build A Killer Client For Your REST+JSON API

Lessons Learned: SyncSync is still easy:

account.getGroups() just delegates to:

account.req().groups()... .get();

Learn more at Stormpath.com

Page 58: Build A Killer Client For Your REST+JSON API

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

$ cd stormpath-sdk-java

$ mvn install

Code

Learn more at Stormpath.com

Page 59: Build A Killer Client For Your REST+JSON API

Thank You!

[email protected]• Twitter: @lhazlewood• http://www.stormpath.com

Learn more at Stormpath.com


Related Documents