Skip to content

Support Javalin async request handling [Context#future(...)] #156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 12, 2023

Conversation

LoonyRules
Copy link
Contributor

This PR adds support for async request handling for Javalin users, using the Context#future(...) method as per the discussion in #154

@SentryMan
Copy link
Collaborator

SentryMan commented Feb 11, 2023

Can you also put an async path on the javalin-jsonb test controller?

@LoonyRules
Copy link
Contributor Author

Can you also put an async path on the javalin-jsonb test controller?

@SentryMan I actually cannot do that as the generator creates 2 global variable fields with the same signature instead of just knowing what fields have been created, and use it, if it detects a same signature would've been created. This is something I mentioned to you on discord a couple of weeks ago but looks like we forgot to make a ticket.

Here's the code change:

  @Hidden
  @Get
  List<HelloDto> getAll() {
    return myService.findAll();
  }

  // Added this method
  @Get("/async")
  CompletableFuture<List<HelloDto>> getAllAsync() {
    return CompletableFuture.supplyAsync(() -> {
      // Simulate a delay as if an actual IO operation is being executed.
      // This also helps ensure that we aren't just getting lucky with timings.
      try {
        Thread.sleep(10L);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }

      return myService.findAll();
    }, Executors.newSingleThreadExecutor()); // Example of how to use a custom executor.
  }

both of these methods would have the same JsonType signature and variable names so that's what causes the error:

  private final JsonType<List<HelloDto>> listHelloDtoJsonType;
  private final JsonType<List<HelloDto>> listHelloDtoJsonType;
  private final JsonType<HelloDto> helloDtoJsonType;

  public HelloController$Route(HelloController controller, Validator validator, Jsonb jsonB) {
    this.controller = controller;
    this.validator = validator;
    this.listHelloDtoJsonType = jsonB.type(HelloDto.class).list(); // GET /hello (getAll)
    this.listHelloDtoJsonType = jsonB.type(HelloDto.class).list(); // GET /hello (getAllAsync)
    this.helloDtoJsonType = jsonB.type(HelloDto.class);
  }

variable listHelloDtoJsonType is already defined in class org.example.myapp.web.HelloController$Route

for (final UType type : jsonTypes.values()) {
for (UType type : jsonTypes.values()) {
// Support for CompletableFuture's.
if (type.isGeneric() && type.mainType().equals("java.util.concurrent.CompletableFuture")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need to add a check here so that the raw type of the future doesn't get written twice.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess either modify the jsontypes map, or create a set that tracks what future types have been written. Other non- types do not get duplicates.

Copy link
Collaborator

@SentryMan SentryMan Feb 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking of doing something like this. Add futureSet as a Set field then modify your for loops like so.

      for (UType type : jsonTypes.values()) {
        // Support for CompletableFuture's.
        if ("java.util.concurrent.CompletableFuture".equals(type.mainType())) {
          type = type.paramRaw();
          if (jsonTypes.containsKey(type.full()) || !futureSet.add(type.full())) {
            continue;
          }
        }
// the rest is fine

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll probably need to take out the throw IllegalStateException from the JsonbUtil and put it into the first for loop in the ControllerWriter.

Copy link
Contributor Author

@LoonyRules LoonyRules Feb 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now sure how I didn't notice the jsonTypes map and that it could be used to fix the issue I came across.

I've moved the CompletableFuture generic validation over to the ControllerMethodWriter for Javalin as it should then provide better context (controller + method) in regards to what is wrongly defined. This is just an assumption though, not sure how Avaje handles exceptions when writing sources.

Async tests in test-javalin-json have also been added.

Copy link
Collaborator

@SentryMan SentryMan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@rbygrave rbygrave added this to the 1.26 milestone Feb 12, 2023
@rbygrave rbygrave self-requested a review February 12, 2023 19:36
@rbygrave rbygrave merged commit a388cea into avaje:master Feb 12, 2023
@rbygrave rbygrave linked an issue Feb 12, 2023 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Javalin async request handling
3 participants