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.

Designing, Implementing, and Using Reactive APIs

2,131 views

Published on

SpringOne Platform 2016
Speakers: Ben Hale; Cloud Foundry Java Lead, Pivotal. Paul Harris; Software Engineer, Pivotal. Stephane Maldini; Project Reactor Leads

The Java community is on the cusp of a major change in programming model. As the industry moves towards high-performance micro-service architectures, the need for a reactive programming model becomes clear. In this session, the lead developers of the Cloud Foundry Java Client will talk about what led them to choose a reactive API. Using that project as a lens, they'll explore how they designed and implemented this API using Project Reactor and what users will expect when using a reactive API. If you are a developer looking to provide reactive APIs, this is your chance to gain the experience of team building a large, production-ready reactive library.

Published in: Technology

Designing, Implementing, and Using Reactive APIs

  1. 1. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Designing, Implementing, and Using Reactive APIs Ben Hale (@nebhale)
 Paul Harris (@twoseat)
  2. 2. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Introduction to Reactive Programming • Reactive is used to broadly define event-driven systems • Moves imperative logic to • asynchronous • non-blocking • functional-style code • Allows stable, scalable access to external systems 2
  3. 3. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Cloud Foundry Java Client • Cloud Foundry is composed of • RESTful APIs • Secured by OAuth • Transmitting large JSON request and response payloads • APIs are low level, often requiring orchestration 3
  4. 4. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Cloud Foundry Java Client • Java has become the preferred language for orchestrating Cloud Foundry • Complex orchestration is difficult when working against generic payloads • A strongly typed Java API is required for maintainability 4
  5. 5. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Generic Request and Response Payloads 5 Map<String, Object> request = new HashMap<>();
 request.put("name", "test-application");
 request.put("application", Paths.get("target/test-application.jar"));
 request.put("buildpack", "java-buildpack");
 this.operations.applications()
 .create(request)
 .map(response -> response.get("id"));
  6. 6. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Strongly Typed Request and Response Payloads 6 this.operations.applications()
 .create(CreateApplicationRequest.builder()
 .name("test-application")
 .application(Paths.get("target/test-application.jar"))
 .buildpack("java-buildpack")
 .build())
 .map(CreateApplicationResponse::getId);
  7. 7. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Cloud Foundry Java Client • The high-latency network interaction is a good fit for a Reactive Design • Encourages asynchronous and highly concurrent usage • Partnering with Project Reactor early influenced both the design of the client and of Reactor 7
  8. 8. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Design
  9. 9. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ When To Use Reactive • Networking • Latency • Failures • Backpressure • Highly Concurrent Operations • Scalability 9
  10. 10. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ What Should a Reactive API Return? • Flux<T> • Returns 0 to N values • Mono<T> • Returns 0 to 1 value • The Java Client often returns Monos containing collections 10
  11. 11. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Using a Flux<T> Response 11 Flux<Application> listApplications() {...}
 
 Flux<String> listApplicationNames() {
 return listApplications()
 .map(Application::getName);
 }
 
 void printApplicationName() {
 listApplicationNames()
 .subscribe(System.out::println);
 }
  12. 12. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Using a Mono<T> Response 12 Flux<Application> listApplications() {...}
 
 Mono<List<String>> listApplicationNames() {
 return listApplications()
 .map(Application::getName) .collectAsList();
 }
 
 Mono<Boolean> doesApplicationExist(String name) {
 return listApplicationNames()
 .map(names -> names.contains(name));
 }
  13. 13. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Every Method Must Return Something • Unlike many imperative APIs, void is not an appropriate return type • A reactive flow declares behavior but does not execute it • Consumers must subscribe to a Flux or Mono in order to begin execution 13
  14. 14. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Imperative Operation 14 void delete(String id) { this.restTemplate.delete(URI, id); }
 
 public static void main(String[] args) {
 delete("test-id");
 }
  15. 15. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reactive Operation 15 Mono<Void> delete(String id) {
 return this.httpClient.delete(URI, id);
 }
 
 public static void main(String[] args) {
 CountDownLatch latch = new CountDownLatch(1);
 
 delete("test-id")
 .subscribe(n -> {}, Throwable::printStackTrace, () -> latch.countDown());
 
 latch.await();
 }
  16. 16. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Scope of a Method • Methods should be designed to be small and reusable • Reusable methods can be more easily composed into larger operations • These methods can be more flexibly combined into parallel or sequential operations 16
  17. 17. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Reusable Methods 17 Mono<ListApplicationsResponse> getPage(int page) {
 return this.client.applicationsV2()
 .list(ListApplicationsRequest.builder()
 .page(page)
 .build());
 }
 
 public static void main(String[] args) {
 getPage(1)
 .map(PaginatedResponse::getTotalPages)
 .flatMap(totalPages -> Flux.range(2, totalPages)
 .flatMap(page -> getPage(page)))
 .subscribe(System.out::println);
 }
  18. 18. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Implement
  19. 19. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Sequential and Parallel Coordination • All significant performance improvements come from concurrency • But, concurrency is very hard to do properly • Reactive frameworks allow you to define sequential and parallel relationships between operations • The framework does the hard work of coordinating the operations 19
  20. 20. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Coordination 20 Mono<ListApplicationsResponse> getPage(int page) {
 return this.client.applicationsV2()
 .list(ListApplicationsRequest.builder()
 .page(page)
 .build());
 }
 
 public static void main(String[] args) {
 getPage(1)
 .map(PaginatedResponse::getTotalPages)
 .flatMap(totalPages -> Flux.range(2, totalPages)
 .flatMap(page -> getPage(page)))
 .subscribe(System.out::println);
 }
  21. 21. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Conditional Logic • Like normal values, errors pass through the flow’s operations • These errors can be passed all the way to consumers, or flows can change behavior based on them • Flows can transform errors or generate new results based on the error 21
  22. 22. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Error Logic 22 public Mono<AppStatsResponse> getApp(GetAppRequest request) {
 return client.applications()
 .statistics(AppStatsRequest.builder()
 .applicationId(request.id())
 .build())
 .otherwise(ExceptionUtils.statusCode(APP_STOPPED_ERROR),
 t -> Mono.just(AppStatsResponse.builder().build()));
 }
  23. 23. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Conditional Logic • It is valid for a flow to complete without sending any elements • This is often equivalent to returning null from an imperative API • This completion can be passed all the way to consumers • Alternatively, flows can switch behavior or generate new elements based on the completion 23
  24. 24. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Empty Logic 24 public Flux<GetDomainsResponse> getDomains(GetDomainsRequest request) {
 return requestPrivateDomains(request.getId())
 .switchIfEmpty(requestSharedDomains(request.getId));
 }
  25. 25. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Empty Logic 25 public Mono<String> getDomainId(GetDomainIdRequest request) {
 return getPrivateDomainId(request.getName())
 .otherwiseIfEmpty(getSharedDomainId(request.getName()))
 .otherwiseIfEmpty(ExceptionUtils.illegalState( "Domain %s not found", request.getName()));
 }
  26. 26. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Conditional Logic • Sometimes you want to make decisions not based on errors or emptiness, but on values themselves • While it’s possible to implement this logic using operators, it often proves to be more complex than is worthwhile • It’s OK to use imperative conditional statements 26
  27. 27. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Conditional Logic Avoiding Imperative 27 public Mono<String> getDomainId(String domain, String organizationId) {
 return Mono.just(domain)
 .filter(d -> d == null)
 .then(getSharedDomainIds()
 .switchIfEmpty(getPrivateDomainIds(organizationId))
 .next()
 .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain not found")))
 .otherwiseIfEmpty(getPrivateDomainId(domain, organizationId)
 .otherwiseIfEmpty(getSharedDomainId(domain))
 .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain %s not found", domain)));
 }
  28. 28. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Conditional Logic Using Imperative 28 public Mono<String> getDomainId(String domain, String organizationId) {
 if (domain == null) {
 return getSharedDomainIds()
 .switchIfEmpty(getPrivateDomainIds(organizationId))
 .next()
 .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain not found"));
 } else {
 return getPrivateDomainId(domain, organizationId)
 .otherwiseIfEmpty(getSharedDomainId(domain))
 .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain %s not found", domain));
 }
 }
  29. 29. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Testing • Most useful flows will be asynchronous • Testing frameworks are aggressively synchronous, registering results before asynchronous results return • To compensate for this you must block the main thread and make test assertions in the asynchronous thread 29
  30. 30. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Test Finishing Before Assertions 30 @Test
 public void noLatch() {
 Mono.just("alpha")
 .subscribeOn(Schedulers.computation())
 .subscribe(s -> assertEquals("bravo", s));
 }
  31. 31. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Test Finishing Before Assertions 31 @Test
 public void noLatch() {
 Mono.just("alpha")
 .subscribeOn(Schedulers.computation())
 .subscribe(s -> assertEquals("bravo", s));
 }
  32. 32. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ @Test
 public void latch() throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 AtomicReference<String> actual = new AtomicReference<>();
 
 Mono.just("alpha")
 .subscribeOn(Schedulers.computation())
 .subscribe(actual::set, t -> latch.countDown(), latch::countDown);
 
 latch.await();
 assertEquals("bravo", actual.get());
 } Test Blocking Using CountDownLatch 32
  33. 33. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ @Test
 public void latch() throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 AtomicReference<String> actual = new AtomicReference<>();
 
 Mono.just("alpha")
 .subscribeOn(Schedulers.computation())
 .subscribe(actual::set, t -> latch.countDown(), latch::countDown);
 
 latch.await();
 assertEquals("bravo", actual.get());
 } Test Blocking Using CountDownLatch 33
  34. 34. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Test Subscriber • Testing a Reactive design requires more than just blocking • Multiple value assertion • Expected error assertion • Unexpected error failure • A TestSubscriber<T> can collect this behavior into a single type 34
  35. 35. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Test Using TestSubscriber<T> 35 @Test
 public void testSubscriber() throws InterruptedException {
 TestSubscriber<String> testSubscriber = new TestSubscriber<>();
 
 Flux.just("alpha", "bravo")
 .subscribeOn(Schedulers.computation())
 .subscribe(testSubscriber
 .assertEquals("alpha")
 .assertEquals("bravo"));
 
 testSubscriber.verify(ofSeconds(5));
 }
  36. 36. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Test Using TestSubscriber<T> 36 @Test
 public void testSubscriber() throws InterruptedException {
 TestSubscriber<String> testSubscriber = new TestSubscriber<>();
 
 Flux.just("alpha", "bravo")
 .subscribeOn(Schedulers.computation())
 .subscribe(testSubscriber
 .assertEquals("alpha")
 .assertEquals("bravo"));
 
 testSubscriber.verify(ofSeconds(5));
 }
  37. 37. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Use
  38. 38. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Countdown Latches • Reactive frameworks can only coordinate their own operations • Many execution environments have processes that outlast individual threads • Reactive programming plays nicely here • Some execution environments aggressively terminate the process before any individual thread 38
  39. 39. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ main() Finishing Before Subscriber<T> 39 public static void main(String[] args) {
 Mono.just("alpha")
 .delaySubscription(Duration.ofSeconds(1))
 .subscribeOn(Schedulers.computation())
 .subscribe(System.out::println);
 }
  40. 40. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ main() Finishing Before Subscriber<T> 40 public static void main(String[] args) {
 Mono.just("alpha")
 .delaySubscription(Duration.ofSeconds(1))
 .subscribeOn(Schedulers.computation())
 .subscribe(System.out::println);
 }
  41. 41. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ main() Waiting for Subscriber<T> 41 public static void main(String[] args) throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 
 Mono.just("alpha")
 .delaySubscription(Duration.ofSeconds(1))
 .subscribeOn(Schedulers.computation())
 .subscribe(System.out::println, t -> latch.countDown(), latch::countDown);
 
 latch.await();
 }
  42. 42. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ main() Waiting for Subscriber<T> 42 public static void main(String[] args) throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 
 Mono.just("alpha")
 .delaySubscription(Duration.ofSeconds(1))
 .subscribeOn(Schedulers.computation())
 .subscribe(System.out::println, t -> latch.countDown(), latch::countDown);
 
 latch.await();
 }
  43. 43. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Blocking flows • It’s very common to bridge between imperative and reactive APIs • In those cases blocking while waiting for a result is appropriate. 43
  44. 44. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Bridging Imperative and Reactive using block() 44 Mono<User> requestUser(String name) {...}
 @Override
 User requestUser(String name) {
 return getUser(name)
 .block();
 }
  45. 45. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Bridging Imperative and Reactive using block() 45 Flux<User> requestUsers() {...}
 @Override
 List<User> listUsers() {
 return requestUsers()
 .collectList()
 .block();
 }
  46. 46. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Error Handling • Because errors are values they must be handled, they do not just show up • Subscribe has 0…3 parameters • Release latches on error 46
  47. 47. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Error Handling 47 public static void main(String[] args) throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 
 Flux.concat(Mono.just("alpha"), Mono.error(new IllegalStateException()))
 .subscribe(System.out::println, t -> {
 t.printStackTrace();
 latch.countDown();
 }, latch::countDown);
 
 latch.await();
 }
  48. 48. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Composable Method References • Reactive programming is susceptible to callback hell, but it doesn’t have to be • Extracting behavior into private methods can be even more valuable than in imperative programming • Well named methods, and method reference syntax, lead to very readable flows 48
  49. 49. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Composable Method References 49 @Override
 public Flux<ApplicationSummary> list() {
 return Mono
 .when(this.cloudFoundryClient, this.spaceId)
 .then(function(DefaultApplications::requestSpaceSummary))
 .flatMap(DefaultApplications::extractApplications)
 .map(DefaultApplications::toApplicationSummary);
 }
  50. 50. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Point Free • You may have noticed that our code samples use a very compact style • Point Free (http://bit.ly/Point-Free) • It helps the developer think about composing functions (high level), rather than shuffling data (low level) • You don’t have to use, but we find that most people prefer it (eventually) 50
  51. 51. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Point Free Style 51 Mono<Void> deleteApplication(String name) {
 return PaginationUtils
 .requestClientV2Resources(page -> this.client.applicationsV2()
 .list(ListApplicationsRequest.builder()
 .name(name)
 .page(page)
 .build()))
 .single()
 .map(applicationResource -> applicationResource.getMetadata().getId())
 .then(applicationId -> this.client.applicationsV2()
 .delete(DeleteApplicationRequest.builder()
 .applicationId(applicationId)
 .build()));
 }
  52. 52. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Summary
  53. 53. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Designing, Implementing and Using Reactive APIs • Design • When to use • Return Mono/Flux • Reusable Methods • Implement • Sequential and Parallel • Conditional logic • Testing
 • Use • Countdown Latches • Blocking flows • Error Handling • Composable methods • Point Free Style 53
  54. 54. Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Learn More. Stay Connected. https://github.com/cloudfoundry/cf-java-client @springcentral spring.io/blog @pivotal pivotal.io/blog @pivotalcf http://engineering.pivotal.io

×