[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