[External] : Re: Changes to JEP 453

Ron Pressler ron.pressler at oracle.com
Wed May 24 09:00:11 UTC 2023


Okay, so we should try to avoid ever directly dealing with a particular subtask’s exceptions; in fact we try to avoid ever calling any method on Subtask other than get. If you find yourself calling any other Subtask method (outside handleComplete) — rethink. 

So here’s how I might do it (assuming you really don’t want to wrap the forks’ exceptions; also, sorry, but I couldn’t bring myself to wrapping InterruptedException with a different type):

static MailData getMailData(long customerId, long productId) throws ApplicationException, InterruptedException {
    var customerService = new CustomerService();
    var productService = new ProductService();

    try (var sts = new StructuredTaskScope.ShutdownOnFailure()) {
        var customerTask = sts.fork(() -> customerService.findCustomerFromId(customerId));
        var productDescriptionTask = sts.fork(() -> {
            try { return Optional.of(productService.findProductDescription(productId)); }
            catch (ApplicationException e) { return Optional.<ProductDescription>empty(); }
        });
    
        sts.join().throwIfFailed(e -> e);
    
        var customer = customerTask.get();
        var productDescription = productDescriptionTask.get()
             .orElse(getDefaultDescription(customer.locale());
    
        return new MailData(customer.name(), productDescription);
    } catch (ApplicationException|InterruptedException|Error e) {
        throw e;
    } catch (Throwable e) {
        throw new RuntimeException(e);
    }
}

Note that it also shuts down correctly if CustomerService throws.

— Ron

> On 23 May 2023, at 22:00, forax at univ-mlv.fr wrote:
> 
> 
> Let say I've two services, one that give me a Customer from a customer id and another that give me the product description from a product id,
> and i want to send a mail containing the customer name and a description.
> Alternative scenario, if the product service reports an error, a default description is provided depending on the language of the customer.
> 
> record Customer(long id, String name, Locale locale) {}
> record ProductDescription(long id, String description) {}
> 
> static sealed class ApplicationException extends Exception { ... }
> static final class ServiceErrorException extends ApplicationException { ... }
> static final class CustomerNotFoundException extends ApplicationException { ... }
> 
> record CustomerService() {
> Customer findCustomerFromId(long id) throws ApplicationException {
> try {
> Thread.sleep(1_000);
> } catch (InterruptedException e) {
> throw new ServiceErrorException(e);
> }
> if (id == 0) {
> throw new CustomerNotFoundException();
> }
> return new Customer(id, "Bob", Locale.of("fr")); // fake data
> }
> }
> 
> record ProductService() {
> ProductDescription findProductDescription(long id) throws ApplicationException {
> try {
> Thread.sleep(1_000);
> } catch (InterruptedException e) {
> throw new ServiceErrorException(e);
> }
> return new ProductDescription(id, "description"); // fake data
> }
> }
> 
> private static String getDefaultDescription(Locale locale) {
> return "fake data";
> }
> 
> record MailData(String customerName, String productDescription) {}
> 
> static MailData getMailData(long customerId, long productId) throws ApplicationException {
> var customerService = new CustomerService();
> var productService = new ProductService();
> 
> try(var sts = new StructuredTaskScope<Object, ApplicationException>()) {
> // both calls can be done in parallel
> var customerTask = sts.fork(() -> customerService.findCustomerFromId(customerId));
> var productDescriptionTask = sts.fork(() -> productService.findProductDescription(productId));
> 
> sts.join();
> 
> // the business logic is implemented here
> var customer = customerTask.result(); // should propagate the exception transparently
> String productDescription;
> try {
> productDescription = productDescriptionTask.result().description();
> } catch(ApplicationException e) { // the exception type is not erased
> productDescription = getDefaultDescription(customer.locale());
> }
> return new MailData(customer.name(), productDescription);
> } catch (InterruptedException e) {
> throw new ServiceErrorException(e);
> }
> }
> 
> 
> findCustomerfromId, findProductDescription and getMailData() all throw an ApplicationException.
> getMailData() can itself be called in another STS, composition is easy !
> 
> Exceptions naturally flows with no wrapping by default.
> 
> [...]
> 
>> 
>> — Ron
> 
> Rémi



More information about the loom-dev mailing list