├── services ├── src │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── com │ │ └── balarawool │ │ └── bootloom │ │ └── abank │ │ └── services │ │ ├── ServicesApplication.java │ │ ├── domain │ │ └── Model.java │ │ ├── util │ │ └── ServicesUtil.java │ │ └── CustomerController.java └── pom.xml ├── abank ├── src │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── com │ │ └── balarawool │ │ └── bootloom │ │ └── abank │ │ ├── controller │ │ ├── HelloController.java │ │ └── LoanController.java │ │ ├── ABankApplication.java │ │ ├── domain │ │ ├── Model.java │ │ └── RequestContext.java │ │ └── service │ │ ├── CustomerService.java │ │ ├── AccountService.java │ │ ├── CreditScoreService.java │ │ └── LoanService.java └── pom.xml ├── .gitignore ├── README.md └── pom.xml /services/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8081 2 | 3 | spring.application.name=SpringBootLoomServices 4 | spring.threads.virtual.enabled=true 5 | -------------------------------------------------------------------------------- /abank/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8082 2 | 3 | spring.application.name=ABank 4 | spring.threads.virtual.enabled=true 5 | 6 | abank.services.base-url=http://localhost:8081 7 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/controller/HelloController.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.controller; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | public class HelloController { 8 | 9 | @GetMapping("/hello") 10 | public String hello() { 11 | return "Hello from "+Thread.currentThread(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /services/src/main/java/com/balarawool/bootloom/abank/services/ServicesApplication.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.services; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ServicesApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ServicesApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /services/src/main/java/com/balarawool/bootloom/abank/services/domain/Model.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.services.domain; 2 | 3 | import java.util.List; 4 | 5 | public interface Model { 6 | 7 | record Customer(String id) { } 8 | record Account(String number, String balance) { } 9 | record Loan(String number, String amount) { } 10 | record CreditScore(String score) { } 11 | record LoanOfferRequest(List accounts, List loans, String creditScore, String amount, String purpose) { } 12 | record Offer(String id, String amount, String purpose, String interest, String offerText) { } 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | /.idea/ 15 | 16 | ### Eclipse ### 17 | .apt_generated 18 | .classpath 19 | .factorypath 20 | .project 21 | .settings 22 | .springBeans 23 | .sts4-cache 24 | 25 | ### NetBeans ### 26 | /nbproject/private/ 27 | /nbbuild/ 28 | /dist/ 29 | /nbdist/ 30 | /.nb-gradle/ 31 | build/ 32 | !**/src/main/**/build/ 33 | !**/src/test/**/build/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | 38 | ### Mac OS ### 39 | .DS_Store 40 | 41 | -------------------------------------------------------------------------------- /services/src/main/java/com/balarawool/bootloom/abank/services/util/ServicesUtil.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.services.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class ServicesUtil { 7 | private static final Logger logger = LoggerFactory.getLogger(ServicesUtil.class); 8 | 9 | public static void logAndWait(String task) { 10 | long delay = 1 + (long)(Math.random() * 5); 11 | logger.info("Performing task: {}(). Time to complete: {} seconds", task, delay); 12 | try { 13 | Thread.sleep(delay * 1_000); 14 | logger.info("Done task: {}()", task); 15 | } catch (InterruptedException e) { 16 | throw new RuntimeException(e); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/ABankApplication.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.web.client.RestClient; 8 | 9 | @SpringBootApplication 10 | public class ABankApplication { 11 | 12 | @Value("${abank.services.base-url}") 13 | private String servicesBaseUrl; 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(ABankApplication.class, args); 17 | } 18 | 19 | @Bean 20 | public RestClient restClient(RestClient.Builder restClientBuilder) { 21 | return restClientBuilder.baseUrl(servicesBaseUrl).build(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /services/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.balarawool.bootloom 8 | SpringBootLoom 9 | 1.0-SNAPSHOT 10 | 11 | 12 | com.balarawool.bootloom.services 13 | services 14 | 15 | 16 | 25 17 | 25 18 | UTF-8 19 | 20 | 21 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/domain/Model.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.domain; 2 | 3 | import java.util.List; 4 | 5 | public interface Model { 6 | 7 | record LoanApplicationRequest(String customerId, String amount, String purpose) { } 8 | record Customer(String id) { } 9 | record Account(String number, String balance) { } 10 | record Loan(String number, String amount) { } 11 | record CreditScore(String score) { } 12 | record LoanOfferRequest(List accounts, List loans, String creditScore, String amount, String purpose) { } 13 | record Offer(String id, String amount, String purpose, String interest, String offerText) { } 14 | 15 | class ABankException extends RuntimeException { 16 | public ABankException(String message) { 17 | super(message); 18 | } 19 | 20 | public ABankException(Throwable th) { 21 | super(th); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Virtual Threads, Structured Concurrency and Scoped Values: Putting it all together 2 | 3 | This is source code for my talk 'Virtual Threads, Structured Concurrency and Scoped Values: Putting it all together'. 4 | If you have any questions, comments or feedback, contact me at [@balarawool.bsky.social](https://bsky.app/profile/balarawool.bsky.social) 5 | 6 | ## Description 7 | 8 | This Maven project has two modules: `abank` and `services` 9 | - `abank` module is a spring-boot app that makes use of features of Project Loom: Virtual Threads, Structured Concurrency and Scoped Values. 10 | - `services` module is another a spring-boot app that contains external services used by `abank` module with some dummy responses. 11 | 12 | ## Pre-requisites 13 | 14 | This project requires JDK 25 (Project Loom early-access build). 15 | You can download Project Loom early-access builds from [https://jdk.java.net/loom/](https://jdk.java.net/loom/) 16 | 17 | Also, make sure to set `--enable-preview` as VM argument while running these spring-boot apps. 18 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/domain/RequestContext.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.domain; 2 | 3 | import com.balarawool.bootloom.abank.domain.Model.Customer; 4 | 5 | import java.lang.ScopedValue.CallableOp; 6 | import java.util.UUID; 7 | 8 | public class RequestContext { 9 | 10 | private static final ScopedValue REQUEST_ID = ScopedValue.newInstance(); 11 | 12 | public static Request withRequestId(UUID requestId) { 13 | return new Request(ScopedValue.where(REQUEST_ID, requestId)); 14 | } 15 | 16 | public static UUID getRequestId() { 17 | return REQUEST_ID.orElseThrow(() -> new Model.ABankException("No request ID available")); 18 | } 19 | 20 | public static class Request { 21 | private ScopedValue.Carrier carrier; 22 | 23 | private Request(ScopedValue.Carrier carrier) { 24 | this.carrier = carrier; 25 | } 26 | 27 | public T call(CallableOp callableOp) throws X { 28 | return carrier.call(callableOp); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/service/CustomerService.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.service; 2 | 3 | import com.balarawool.bootloom.abank.domain.Model.Customer; 4 | import com.balarawool.bootloom.abank.domain.RequestContext; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.web.client.RestClient; 9 | 10 | @Service 11 | public class CustomerService { 12 | private static final Logger logger = LoggerFactory.getLogger(CustomerService.class); 13 | 14 | private RestClient restClient; 15 | 16 | public CustomerService(RestClient restClient) { 17 | this.restClient = restClient; 18 | } 19 | 20 | public Customer getCustomer(String customerId) { 21 | var requestId = RequestContext.getRequestId(); 22 | 23 | logger.info("{} CustomerService.getCustomer(): Start", requestId); 24 | 25 | var customer = restClient.get().uri("/customer/{id}", customerId).retrieve().body(Customer.class); 26 | 27 | logger.info("{} CustomerService.getCustomer(): Done", requestId); 28 | return customer; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /abank/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.balarawool.bootloom 8 | SpringBootLoom 9 | 1.0-SNAPSHOT 10 | 11 | 12 | com.balarawool.bootloom.abank 13 | abank 14 | 15 | 16 | 25 17 | 25 18 | UTF-8 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 26 | 25 27 | 25 28 | --enable-preview 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/service/AccountService.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.service; 2 | 3 | import com.balarawool.bootloom.abank.domain.Model.Account; 4 | import com.balarawool.bootloom.abank.domain.Model.Customer; 5 | import com.balarawool.bootloom.abank.domain.RequestContext; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.core.ParameterizedTypeReference; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.web.client.RestClient; 11 | 12 | import java.util.List; 13 | 14 | @Service 15 | public class AccountService { 16 | private static final Logger logger = LoggerFactory.getLogger(AccountService.class); 17 | 18 | private RestClient restClient; 19 | 20 | public AccountService(RestClient restClient) { 21 | this.restClient = restClient; 22 | } 23 | 24 | public List getAccountsInfo(Customer customer) { 25 | var requestId = RequestContext.getRequestId(); 26 | 27 | logger.info("{} AccountService.getAccountsInfo(): Start", requestId); 28 | 29 | var accounts = restClient 30 | .get() 31 | .uri("/customer/{id}/accounts", customer.id()) 32 | .retrieve() 33 | .body(new ParameterizedTypeReference>() { }); 34 | 35 | logger.info("{} AccountService.getAccountsInfo(): Done", requestId); 36 | return accounts; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.5.1 10 | 11 | 12 | com.balarawool.bootloom 13 | SpringBootLoom 14 | 1.0-SNAPSHOT 15 | pom 16 | 17 | abank 18 | services 19 | 20 | 21 | 22 | 25 23 | 25 24 | 25 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/service/CreditScoreService.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.service; 2 | 3 | import com.balarawool.bootloom.abank.domain.Model.ABankException; 4 | import com.balarawool.bootloom.abank.domain.Model.CreditScore; 5 | import com.balarawool.bootloom.abank.domain.Model.Customer; 6 | import com.balarawool.bootloom.abank.domain.RequestContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.web.client.RestClient; 11 | 12 | import java.util.concurrent.StructuredTaskScope; 13 | import java.util.concurrent.StructuredTaskScope.Joiner; 14 | 15 | @Service 16 | public class CreditScoreService { 17 | private static final Logger logger = LoggerFactory.getLogger(CreditScoreService.class); 18 | 19 | private RestClient restClient; 20 | 21 | public CreditScoreService(RestClient restClient) { 22 | this.restClient = restClient; 23 | } 24 | 25 | public CreditScore getCreditScore(Customer customer) { 26 | try (var scope = StructuredTaskScope.open(Joiner.anySuccessfulResultOrThrow())) { 27 | scope.fork(() -> getCreditScoreFrom("credit-score1", customer)); 28 | scope.fork(() -> getCreditScoreFrom("credit-score2", customer)); 29 | 30 | var score = scope.join(); 31 | return score; 32 | } catch (InterruptedException e) { 33 | throw new ABankException(e); 34 | } 35 | } 36 | 37 | private CreditScore getCreditScoreFrom(String endpoint, Customer customer) { 38 | var requestId = RequestContext.getRequestId(); 39 | 40 | logger.info("{} CreditScoreService.getCreditScore() with {}: Start", requestId, endpoint); 41 | 42 | var score = restClient.get().uri("/customer/{id}/{endpoint}", customer.id(), endpoint).retrieve().body(CreditScore.class); 43 | logger.info("{} CreditScoreService.getCreditScore() with {}: Done", requestId, endpoint); 44 | 45 | return score; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/service/LoanService.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.service; 2 | 3 | import com.balarawool.bootloom.abank.domain.Model.Account; 4 | import com.balarawool.bootloom.abank.domain.Model.CreditScore; 5 | import com.balarawool.bootloom.abank.domain.Model.Customer; 6 | import com.balarawool.bootloom.abank.domain.Model.Loan; 7 | import com.balarawool.bootloom.abank.domain.Model.LoanOfferRequest; 8 | import com.balarawool.bootloom.abank.domain.Model.Offer; 9 | import com.balarawool.bootloom.abank.domain.RequestContext; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.core.ParameterizedTypeReference; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.web.client.RestClient; 15 | 16 | import java.util.List; 17 | 18 | @Service 19 | public class LoanService { 20 | private static final Logger logger = LoggerFactory.getLogger(LoanService.class); 21 | 22 | private RestClient restClient; 23 | 24 | public LoanService(RestClient restClient) { 25 | this.restClient = restClient; 26 | } 27 | 28 | public List getLoansInfo(Customer customer) { 29 | var requestId = RequestContext.getRequestId(); 30 | 31 | logger.info("{} LoanService.getLoansInfo(): Start", requestId); 32 | 33 | var loans = restClient 34 | .get() 35 | .uri("/customer/{id}/loans", customer.id()) 36 | .retrieve() 37 | .body(new ParameterizedTypeReference>() { }); 38 | 39 | logger.info("{} LoanService.getLoansInfo(): Done", requestId); 40 | return loans; 41 | } 42 | 43 | public Offer calculateOffer(Customer customer, 44 | List accountsInfo, 45 | List loansInfo, 46 | CreditScore creditScore, 47 | String amount, 48 | String purpose) { 49 | var requestId = RequestContext.getRequestId(); 50 | 51 | logger.info("{} LoanService.calculateOffer(): Start", requestId); 52 | 53 | var loanOfferRequest = new LoanOfferRequest(accountsInfo, loansInfo, creditScore.score(), amount, purpose); 54 | var offer = restClient 55 | .post() 56 | .uri("/customer/{id}/loans/offer", customer.id()) 57 | .body(loanOfferRequest) 58 | .retrieve() 59 | .body(Offer.class); 60 | 61 | logger.info("{} LoanService.calculateOffer(): Done", requestId); 62 | return offer; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /services/src/main/java/com/balarawool/bootloom/abank/services/CustomerController.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.services; 2 | 3 | import com.balarawool.bootloom.abank.services.domain.Model.Account; 4 | import com.balarawool.bootloom.abank.services.domain.Model.CreditScore; 5 | import com.balarawool.bootloom.abank.services.domain.Model.Customer; 6 | import com.balarawool.bootloom.abank.services.domain.Model.Loan; 7 | import com.balarawool.bootloom.abank.services.domain.Model.LoanOfferRequest; 8 | import com.balarawool.bootloom.abank.services.domain.Model.Offer; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PathVariable; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import java.util.List; 17 | 18 | import static com.balarawool.bootloom.abank.services.util.ServicesUtil.logAndWait; 19 | 20 | @RestController 21 | @RequestMapping("/customer") 22 | public class CustomerController { 23 | 24 | @GetMapping("/{id}") 25 | public Customer getCustomer(@PathVariable("id") String customerId) { 26 | logAndWait("getCustomer"); 27 | return new Customer(customerId); 28 | } 29 | 30 | @GetMapping("/{id}/credit-score1") 31 | public CreditScore getCreditScore1(@PathVariable("id") String customerId) { 32 | logAndWait("getCreditScore1"); 33 | return new CreditScore("Score1"); 34 | } 35 | 36 | @GetMapping("/{id}/credit-score2") 37 | public CreditScore getCreditScore2(@PathVariable("id") String customerId) { 38 | logAndWait("getCreditScore2"); 39 | return new CreditScore("Score2"); 40 | } 41 | 42 | @GetMapping("/{id}/accounts") 43 | public List getAccountsInfo(@PathVariable("id") String customerId) { 44 | logAndWait("getAccountsInfo"); 45 | return List.of(new Account("123", "1000.00"), new Account("456", "2000.00")); 46 | } 47 | 48 | @GetMapping("/{id}/loans") 49 | public List getLoansInfo(@PathVariable("id") String customerId) { 50 | logAndWait("getLoansInfo"); 51 | return List.of(new Loan("TL123", "10000.00"), new Loan("CL456", "20000.00")); 52 | } 53 | 54 | @PostMapping("/{id}/loans/offer") 55 | public Offer calculateOffer(@PathVariable("id") String customerId, @RequestBody LoanOfferRequest request) { 56 | logAndWait("calculateOffer"); 57 | return new Offer("LMN123", request.amount(), request.purpose(), "4.00","An offer for your loan application"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /abank/src/main/java/com/balarawool/bootloom/abank/controller/LoanController.java: -------------------------------------------------------------------------------- 1 | package com.balarawool.bootloom.abank.controller; 2 | 3 | import com.balarawool.bootloom.abank.domain.Model.ABankException; 4 | import com.balarawool.bootloom.abank.domain.Model.Account; 5 | import com.balarawool.bootloom.abank.domain.Model.CreditScore; 6 | import com.balarawool.bootloom.abank.domain.Model.Customer; 7 | import com.balarawool.bootloom.abank.domain.Model.Loan; 8 | import com.balarawool.bootloom.abank.domain.Model.LoanApplicationRequest; 9 | import com.balarawool.bootloom.abank.domain.Model.Offer; 10 | import com.balarawool.bootloom.abank.domain.RequestContext; 11 | import com.balarawool.bootloom.abank.service.AccountService; 12 | import com.balarawool.bootloom.abank.service.CreditScoreService; 13 | import com.balarawool.bootloom.abank.service.CustomerService; 14 | import com.balarawool.bootloom.abank.service.LoanService; 15 | import org.springframework.web.bind.annotation.PostMapping; 16 | import org.springframework.web.bind.annotation.RequestBody; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | import java.util.List; 20 | import java.util.UUID; 21 | import java.util.concurrent.StructuredTaskScope; 22 | 23 | @RestController 24 | public class LoanController { 25 | private CustomerService customerService; 26 | private AccountService accountService; 27 | private LoanService loanService; 28 | private CreditScoreService creditScoreService; 29 | 30 | public LoanController(CustomerService customerService, 31 | AccountService accountService, 32 | LoanService loanService, 33 | CreditScoreService creditScoreService) { 34 | this.customerService = customerService; 35 | this.accountService = accountService; 36 | this.loanService = loanService; 37 | this.creditScoreService = creditScoreService; 38 | } 39 | 40 | @PostMapping("/loan-application") 41 | public Offer applyForLoan(@RequestBody LoanApplicationRequest request) { 42 | var requestID = UUID.randomUUID(); 43 | 44 | return RequestContext.withRequestId(requestID) 45 | .call(() -> { 46 | var currentCustomer = customerService.getCustomer(request.customerId()); 47 | 48 | var customerInfo = getCustomerInfo(currentCustomer); 49 | var offer = loanService.calculateOffer( 50 | currentCustomer, customerInfo.accounts(), customerInfo.loans(), customerInfo.creditScore(), request.amount(), request.purpose() 51 | ); 52 | return offer; 53 | }); 54 | } 55 | 56 | private record CustomerInfo(List accounts, List loans, CreditScore creditScore) { } 57 | private CustomerInfo getCustomerInfo(Customer customer) { 58 | try (var scope = StructuredTaskScope.open()) { 59 | var task1 = scope.fork(() -> accountService.getAccountsInfo(customer)); 60 | var task2 = scope.fork(() -> loanService.getLoansInfo(customer)); 61 | var task3 = scope.fork(() -> creditScoreService.getCreditScore(customer)); 62 | 63 | scope.join(); 64 | var accountsInfo = task1.get(); 65 | var loansInfo = task2.get(); 66 | var creditScore = task3.get(); 67 | 68 | return new CustomerInfo(accountsInfo, loansInfo, creditScore); 69 | } catch (InterruptedException e) { 70 | throw new ABankException(e); 71 | } 72 | } 73 | } 74 | --------------------------------------------------------------------------------