3

There is one thing I have trouble understanding with quarkus. I use JPA with Oracle. So I have the error IllegalStateException: You have attempted to perform a blocking operation on an IO thread. This is not allowed, as blocking the IO thread I looked in the Quarkus doc how to go about making JPA calls without having this difficulty. But all the examples and the doc use PostgreSQL or MariaDB with responsive clients. But I have not found any for classic JDBC clients.

I found a solution that works partially.

Uni.cretaFrom().Items(() -> MyBlokingIOCall());

Indeed I no longer have the exception. But the MyBlokingIOCall method can raise exceptions. I think I can use Uni's onFailure but we cannot pass methods raising an exception to

Uni.cretaFrom().Items

Indeed this method has as argument a Supplier, therefore the exceptions must be captured. it's impossible to use Uni's onFailure with this solution. If I use the current code in imperative mode

try {
   Response.ok (myService ());
} catch (throwable e) {
   Response.status (555, e.getMessage ());
}

I tried in vain to do this in reactive form but I did not find a solution. I did not find any help. I imagined something like.

Uni.createFrom().Items(() -> myService())
.onItem().apply(data -> Response.ok(data))
.onFailure().apply(err -> Response.status(555, err.getMessage()))

But this causes a compilation error. indeed the method myService cannot be used in a Supplier because it throws an exception. it must therefore be captured. But then we no longer go into Uni's onFailure and we cannot trace the error we caught.

I think I think too much about wearing my imperative code instead of thinking differently.

But I can't find a doc or example that looks like this by any means. I imagine that there is a way of doing things (if not Quarkus would not exist). I think that when you think about it with the right approach, you have to find the doc, but when you don't know where to go, it's more complicated. I haven't found it because I don't know what I'm looking for.

I suppose I have to encapsulate the JPA call which produces a Uni or a Multi which is consumed by a processor which transforms the Entity into DTO which is then transmitted to the resource Rest layer which transforms the DTO into Response. the JPA call must be able to produce errors the processor too and the Resource layer too

It is, therefore, necessary at each step to capture these errors to propagate them through Uni or Multi

But how to do it?

tmarwen
  • 15,750
  • 5
  • 43
  • 62
Sekaijin
  • 107
  • 1
  • 6

2 Answers2

2

This is kind of an know lambda limitation when it comes to conciseness and first expectations by simply providing the lambda implementation (or a method reference) as a method call parameter.

Meanwhile, it can be simply worked-around by implementing an extension around the expected Functional Interface, in your case it would be the Supplier<T>, that will catch any ** checked** exception and throw an unchecked one:

/**
 * A Supplier sub-interface that catches any thrown exception
 * and translates to an unchecked one.
 *
 * <p>This is a functional interface whose functional method is
 * {@link #getChecked()}.
 */
@FunctionalInterface
public interface CheckedSupplier<T> extends Supplier<T> {

    @Override
    default T get() {
        try {
            return getChecked();
        } catch (final Exception e) {
            // Your common exception handling logic here..
            throw new RuntimeException(e);
        }
    }

    /**
     * Gets a result.
     *
     * @return a result
     * @throws Exception
     */
    T getChecked() throws Exception;
}

Note that the functional interface signature is no longer #get() but #getChecked(). But that would make no difference for the compiler, which will try to check the functional signature based on the expected one and which is:

a method returning a T-typed object

Then you can simply use the new CheckedSupplier interface using just an explicit cast wherever a Supplier<T> (or alike) is expected:

Uni.createFrom().item((CheckedSupplier<DataType>) MutinyTest::findOne)
      .onItem()
      .apply(i-> Response.ok(i))
      .onFailure()
      .apply(e-> Response.status(555, e.getMessage()));
tmarwen
  • 15,750
  • 5
  • 43
  • 62
  • Oh thanks I was so focused on my reactive programming problem that I never thought of wrapping the error in a runtimeException – Sekaijin May 22 '20 at 18:15
  • You are welcome, don't hesitate to upvote and accept the answer if that helped fixing the issue ;) – tmarwen May 22 '20 at 18:32
1

I changed my myService method so that it returns a Supplier and handles exceptions

   public Supplier<List<String>> myService (String arg){
      return () -> {
         try {
            //my code
            return result;
         } catch (Exception e) {
            throw new RuntimeException(e);
         }
      };
   }

and I called it that

Uni.createFrom().item(myService("sample")))
.onItem().apply(data -> Response.ok(data))
.onFailure().recoverWithItem(err -> Response.status(600, err.getMessage()))
.onItem().apply(ResponseBuilder::build)
.emitOn(Infrastructure.getDefaultExecutor())
Sekaijin
  • 107
  • 1
  • 6