Skip to content
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

Trial with Gradle BadAss plugin #2132

Open
swar-mukh opened this issue Feb 24, 2024 · 12 comments
Open

Trial with Gradle BadAss plugin #2132

swar-mukh opened this issue Feb 24, 2024 · 12 comments
Assignees

Comments

@swar-mukh
Copy link

Hello,

Given that focus on JPMS is currently ongoing, I am following up on an issue (#1624) that was earlier raised, and its subsequent solution and commentary, as well as a detailed guide on the Community post.

I was researching into a solution to find if there exists any jlink wrapper solutions in the market that can handle "automatic resolutions", and found that a Gradle plugin, awkwardly titled, BadAss (link here), handles the resolutions by itself.

However, when I took it for a spin with Javalin, suprisingly kotlin-stdlib, or org.jetbrains.* wasn't the issue, but the following:

  1. The org.slf4j is not resolved in the final custom JRE image
  2. A web-ui directory under resources cannot be effectively resolved in the final image when using config.staticFiles.add("web-ui", Location.CLASSPATH); (but it works if you normally run the application)

Now, where the issue lies is a bit cloudy. It could be Javalin's or the plugin's issue, or it could even be my setup issue. As a tinkerer and an enthusiast, all I want is for open-source tools to play well with each other and be in harmony. I don't want to miss this opportunity to have a custom JRE image with Javalin embedded in it.

I was wondering if you could provide some insights/assistance into the above, so that once a solution or bug is found, I can (if necessary), raise them in the respective GitHub repos. I am attaching a gradle project which you can run in your local machine.

To reproduce the issues:

  1. Run gradle clean run:
    a. web-ui/ path is found, and app runs
    b. All logging happens as usual
  2. Run gradle clean build jlink:
    a. web-ui/ path is not found, and app runs w/o the static files endpoints
    b. Logging doesn't happen as the dependency is not found

Some other handy references here: #1908, #1947

Attachment: java-webapp.zip

@zugazagoitia
Copy link
Member

zugazagoitia commented Feb 24, 2024

Hey @swar-mukh, thank you for reporting this, We'll look into it.

We currently have no way of testing Javalin JPMS capabilities, so I'll have to dig to see what's going on.

Could you provide the logs for both of the cases? Only runtime ones, the build ones shouldn't be relevant.

The org.slf4j is not resolved in the final custom JRE image

I understand this might be a problem, I'll look into the binaries if you can't provide the logs.

Either way, using the BadAss runtime plugin instead of the JPMS one might just solve it, this problem could be on Jetty's side.

@zugazagoitia zugazagoitia self-assigned this Feb 24, 2024
@zugazagoitia
Copy link
Member

The SLF4j issue should be solved by #2137, and the problem in your case might be a missing resourceDir in the jpackage config. Can you check this?

@SentryMan
Copy link
Contributor

SentryMan commented Feb 24, 2024

The problem in your case might be a missing resourceDir in the jpackage config. Can you check this?

nah that has nothing to do with it. getClass().getResourceAsStream("web-ui") (assuming that's what you use to load) doesn't seem to work on directories inside a Jlink image. specific files work though

@zugazagoitia
Copy link
Member

Any luck @swar-mukh ?

@SentryMan
Copy link
Contributor

SentryMan commented Mar 4, 2024

Any luck @swar-mukh ?

jpackage is its own thing that uses jlink, the problem here is with application java runtime images (JRT) that jlink produces.

I made a repo showcasing the problem using a maven plugin as well as raw jlink: https://github.com/SentryMan/javalin-jlink-issue

It appears Jetty is using getResourceAsStream to load directories, which doesn't fly in a JRT.

@swar-mukh
Copy link
Author

swar-mukh commented Mar 4, 2024

Hi @zugazagoitia, sorry couldn't respond earlier. Was caught up with office work.

I experimented in various ways (limited by Javalin API obviously) with the underlying jlink system either directly or through Gradle and Maven plugins, but both returned the same issue, which is that while packaging, the final image cannot resolve the resources since for some reason they are not present in it. To that end, I second the experimentation result of @SentryMan.

However, here are few things I want to mention:

  1. If you add config.staticFiles.add("web-ui", Location.CLASSPATH);, then post-packaging an extra step has to be run, through your shell, which is to explicitly copy the web-ui directory inside the image/bin/ directory; and more importantly, fire the application, from within that directory itself. This "extra-copy-and-trigger-from-within-bin" seems to resolve the classpath issue. Note: I haven't further tested for CORS (either in dev or in prod mode)
  2. If you add config.staticFiles.add("web-ui", Location.CLASSPATH);, then you have to keep on manually adding all the assets individually, which is not scalable, especially if the developer is using a framework and copying the build to the web-ui, where there is no knowledge beforehand how many dynamic js or css or imgs will be generated.

Now, while the above two points definitely are not resolutions and it is imperative that in someway this has to be resolved, either here or through raising another issue with Jetty (it's your call folks) or through some other repo (which I may not be aware of), I think it is very important in the time-being, that this be documented somewhere in the public website so that people don't have to scour the internet looking for a resolution. I think, we can/may contact the author of #1624 who subsequently had also created a detailed post for some advice or for a contribution in his post so that the quirks of JPMS (especially w.r.t. Javalin) is known to the public.

@SentryMan
Copy link
Contributor

they are not present in it

well to be precise, they are present, but the standard way of accessing resources by directory doesn't work.

"extra-copy-and-trigger-from-within-bin" seems to resolve the classpath issue.

wait are you even still running on the module-path with this method?

Anyway, helidon seems to work around this by not loading the directory. They keep the directory path and append requested resources to that.

So like

routing
    .register("/", StaticContentService.builder("/web-ui")
    .build();

then we can call localhost:8080/index.html and it works.

@swar-mukh
Copy link
Author

well to be precise, they are present,

What I meant was, yes, they are present in the build, except it's not inside the image. And yes, we both agree on the standard way of accessing not working properly. I haven't tested with jars, that's beside the point, but if resolution through jar is also error-prone, then the issue is something else (I give up here).

are you even still running on the module-path with this method?

Like I mentioned, I have experimented it with module-path as well as with the old classpath (forgot the commands now, I'm not on my system), the issue persists. Just pure trial-n-error mode.

helidon seems to work around this by not loading the directory

I am aware of Helidon (and equally with Nima), which apart from inspiration in resource resolution, may not be of much importance to Javalin. This is just my personal opinion.

@SentryMan
Copy link
Contributor

may not be of much importance to Javalin.

Yeah, I'm thinking along the lines of reverse-engineering their resource strategy for javalin

@swar-mukh
Copy link
Author

My original suggestion still stays, which is while the authors/contributors come up with a resolution: in the interest of developers (especially beginners), somewhere in the public website, it should be documented. Once this GitHub issue is resolved, the documentation can be reverted (sans few quirks, of course).

I am not aware of Javalin usage analytics, but suffice to say, it's a beautifully crafted framework, which perhaps necessitates time-boxing this issue (just an opinion). Feel free to dismiss it if you like.

@zugazagoitia
Copy link
Member

We don't provide "first class" support for JPMS, since none of the maintainers use it, and we have no easy way to test it either.

Lucky for you I'm reimplementing Javalin resource handling right now decoupled from Jetty, so I can consider this.

If you can point me to the correct API to use for loading Classpath resources I will gladly implement it.

@SentryMan
Copy link
Contributor

SentryMan commented Mar 4, 2024

It's more changing the approach to not load files via directory. If you want to load everything inside a directory you'll need a specific file within that directory, then from there you can go wild.

  List<Path> getPathsInDirectory(String directoryName, final String initialFile)
      throws URISyntaxException, IOException {

    var path = getClass().getResource(initialFile).toURI();

    // get directory and iterate (will go through nested directories as well)
    return Files.walk(Paths.get(path).getParent())
        .skip(1)
        .filter(
            p -> {
              var pathStr = p.toString().replace("\\", "/");

              pathStr = pathStr.substring(pathStr.indexOf(directoryName));

              return getClass().getResource(pathStr) != null;
            })
        .toList();
  }

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

No branches or pull requests

3 participants