quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.82k stars 2.69k forks source link

Undertow: servletContext.getResourcePaths("/") not listed in PROD mode #28028

Open melloware opened 2 years ago

melloware commented 2 years ago

Describe the bug

I have a simple ApplicationInitializer to print out all the available servlet resource paths...

public class ApplicationInitializer implements ServletContainerInitializer {

    private static final Logger logger = Logger.getLogger(ApplicationInitializer.class.getName());

    @Override
    public void onStartup(Set<Class<?>> c, ServletContext servletContext) throws ServletException {
        logger.info("Checking servlet resource paths...");

        Set<String> resourcePaths = servletContext.getResourcePaths("/");
        for (String resourcePath : resourcePaths) {
            logger.info("Resource: " + resourcePath);
        }

        if (resourcePaths.isEmpty()) {
            logger.warning("NO servlet resource paths found!");
        }
    }
}

When running in DEV mode mvn quarkus:dev the following is printed out it finds the mapped /index.html:

2022-09-17 07:51:08,575 INFO  [org.acm.ApplicationInitializer] (Quarkus Main Thread) Checking servlet resource paths...
2022-09-17 07:51:08,577 INFO  [org.acm.ApplicationInitializer] (Quarkus Main Thread) Resource: /index.html

When running in PRD mode mvn clean package -DskipTests && java -jar target/quarkus-app/quarkus-run.jar it doesn't find anything:

2022-09-17 07:49:05,376 INFO  [org.acm.ApplicationInitializer] (main) Checking servlet resource paths...
2022-09-17 07:49:05,376 WARNING [org.acm.ApplicationInitializer] (main) NO servlet resource paths found!

Expected behavior

All servlet resource paths should be available in both DEV and PRD mode.

Actual behavior

Resource Paths are only available in DEV mode.

How to Reproduce?

quarkus-servlet.zip

  1. Unzip the reproducer attached
  2. Run mvn clean quarkus:dev and see the servlet paths printed out 2022-12-27 13:00:20,489 INFO [org.acm.ApplicationInitializer] (Quarkus Main Thread) Resource: /index.html
  3. Run mvn clean package -DskipTests && java -jar target/quarkus-app/quarkus-run.jar and see the WARNING that no paths found 2022-12-27 13:01:06,297 WARNING [org.acm.ApplicationInitializer] (main) NO servlet resource paths found!

Output of uname -a or ver

OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Output of java -version

OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode)

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.15.1

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.8.5 (3599d3414f046de2324203b78ddcf9b5e4388aa0) Maven home: C:\Tools\apache-maven-3.8.5 Java version: 11.0.10, vendor: AdoptOpenJDK, runtime: C:\Tools\jdk-11.0.10 Default locale: en_US, platform encoding: Cp1252 OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Additional information

No response

melloware commented 2 years ago

cc @sberyozkin @stuartwdouglas

I have a feeling the bug is right here especially since there is a TODO still there about in PRD mode its not scanning the JARs properly for files but it is in DEV mode because its looking at the File Paths.

https://github.com/quarkusio/quarkus/blob/9ed77a64d671b65db866ea6759a7cb06c94bd7d5/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java#L184-L196

melloware commented 1 year ago

@manovotn was hoping you could look at this one?

manovotn commented 1 year ago

@melloware not sure how you arrived at my name as I haven't done much in Undertow, but I could take a look once I am back from X-mass break. I am unlikely to know though as I am unfamiliar with UT codebase :⁠-⁠)

melloware commented 1 year ago

Sorry @manovotn you answered on the other Undertow ticket but it should have been @mkouba !

manovotn commented 1 year ago

No problem. Like I said, I can take a look once I am operational again. I think Martin will be in very much the same situation :⁠-⁠)

mkouba commented 1 year ago

I believe that the problem lies in the DirectoryResource#list() method. It returns an empty list because the KnownPathResourceManager#files looks like ["index.html"]. I don't think that NavigableSet.headSet(String) could be used here but maybe I've overlooked something.

@stuartwdouglas could you pls take a look sometime next year ;-)

melloware commented 1 year ago

@stuartwdouglas bump ?

melloware commented 1 year ago

This bug is still happening in 999-SNAPSHOT.

melloware commented 1 year ago

@stuartwdouglas just found another bug with all your latest changes that was working:

https://github.com/melloware/quarkus-faces/issues/282

Basically I have resources in \META-INF\resources\java\org\primefaces\showcase\convert\CountryConverter.java as I copy the Java files there so they can be displayed. In Windows it can find the resource but in Linux with the exact same JAR file it fails in Linux.

Windows Works:

java -jar target/quarkus-app/quarkus-run.jar

Linux with same JAR fails:

java -jar target/quarkus-app/quarkus-run.jar

Error:

An error occured while initializing MyFaces: Unable to get listed resource java\org\primefaces\showcase\convert\CountryConverter.java from directory  for path  from underlying manager io.undertow.server.handlers.resource.ClassPathResourceManager@5c059a68: java.lang.RuntimeException: Unable to get listed resource java\org\primefaces\showcase\convert\CountryConverter.java from directory  for path  from underlying manager io.undertow.server.handlers.resource.ClassPathResourceManager@5c059a68
2023-06-07 14:24:34     at io.quarkus.undertow.runtime.KnownPathResourceManager$DirectoryResource.list(KnownPathResourceManager.java:151)
2023-06-07 14:24:34     at io.undertow.server.handlers.resource.CachedResource.list(CachedResource.java:110)

Reported: https://github.com/melloware/quarkus-faces/issues/282

melloware commented 1 year ago

I just submitted another fix: https://github.com/quarkusio/quarkus/pull/34611

melloware commented 1 year ago

@stuartwdouglas I finally got to the bottom of this but have no idea how to fix it. I debugged the code in production mode and its finding the resource correctly using KnownPathResourceManager but in Undertow ServletContextImpl.getResourcePaths() it is returning a URLResource and calling Path file = res.getFilePath(); on that URL resource which ALWAYS returns null.

Resource: jar:file:/C:/temp/quarkus-servlet/target/quarkus-app/app/code-with-quarkus-1.0.0-SNAPSHOT.jar!/META-INF/resources/index.html getFilePath(): NULL !!!

Undertow Code: https://github.com/undertow-io/undertow/blob/master/servlet/src/main/java/io/undertow/servlet/spec/ServletContextImpl.java#L278

image

Here is that line in the debugger:

image

So basically a URL resource jar:file:/C:/temp/quarkus-servlet/target/quarkus-app/app/code-with-quarkus-1.0.0-SNAPSHOT.jar!/META-INF/resources/index.html returns NULL for getFilePath()

tony-in-nz commented 1 year ago

Fails for me on Windows (build and deploy to Ubuntu server). Sent the code to mate with a mac to build and deploy on the Ubuntu server and it worked.

melloware commented 1 month ago

I think @gsmet fix here: https://github.com/quarkusio/quarkus/pull/43694 will fix this issue but I will test for real when its released with a real JSF app that has the issue

melloware commented 1 month ago

circling back to this i don't think @gsmet fix fixes this particular issue. I think this code needs to be modified to look in JAR or File Path like FlyWay does like this using ClassPathUtils.consumeAsPaths

ClassPathUtils.consumeAsPaths(Thread.currentThread().getContextClassLoader(), location, path -> {
                Set<String> applicationMigrations = null;
                try {
                    applicationMigrations = FlywayProcessor.this.getApplicationMigrationsFromPath(finalLocation, path);
                } catch (IOException e) {
                    LOGGER.warnv(e,
                            "Can't process files in path %s", path);
                }
                if (applicationMigrations != null) {
                    applicationMigrationResources.addAll(applicationMigrations);
                }
            });