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

jetty-maven-plugin fails to run ServletContainerInitializer on Windows due to URI case comparison bug #5870

Closed
unb opened this issue Jan 9, 2021 · 2 comments
Assignees
Labels
Bug For general bugs on Jetty side Microsoft Windows For issues unique to Microsoft Windows

Comments

@unb
Copy link

unb commented Jan 9, 2021

Jetty 9.4.35.v20201120

Java 1.8.0_191

Windows 10

Maven 3.6.3

The jetty-maven-plugin can fail to run ServletContainerInitializers on Windows due to a URI case comparison bug.

My maven project includes:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-web</artifactId>
    <version>2.14.0</version>
</dependency>

This jar includes:

  • META-INF/services/javax.servlet.ServletContainerInitializer -> org.apache.logging.log4j.web.Log4jServletContainerInitializer
  • META-INF/web-fragment.xml
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                                  http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
              version="3.0" metadata-complete="true">
    <!-- The Log4j web fragment must be loaded before all other fragments. The configuration below should make this
        happen automatically. If you experience problems, try specifying an <absolute-ordering> in your web.xml
        deployment descriptor. -->
    <name>log4j</name>
    <distributable />
    <ordering>
        <before>
            <others />
        </before>
    </ordering>
</web-fragment>

From the debug output below, the log4j-web dependency is passed to Jetty as a File:

[DEBUG] Artifact org.apache.logging.log4j:log4j-web:jar:2.14.0 loaded from c:\maven\repository\org\apache\logging\log4j\log4j-web\2.14.0\log4j-web-2.14.0.jar added to WEB-INF/lib

Later on, the log shows that the Log4jServletContainerInitializer has been excluded:

[DEBUG] Excluded org.apache.logging.log4j.web.Log4jServletContainerInitializer@571a9686 found=false
[DEBUG] org.apache.logging.log4j.web.Log4jServletContainerInitializer@571a9686 is from excluded jar

This log is coming from AnnotationConfiguration.isFromExcludedJar(WebAppContext context, ServletContainerInitializer sci, Resource sciResource) and is occurring because of the following code in which uses URI.equals() to compare URIs - this doesn't treat drive letters as case insensitive.

The relevant variables in to isFromExcludedJar are:

  • sciResource = file:///C:/maven/repository/org/apache/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar
  • orderedJars =
    "file:///c:/maven/repository/org/apache/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar"
    "file:///c:/maven/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar"
    "file:///c:/maven/repository/org/apache/logging/log4j/log4j-api/2.14.0/log4j-api-2.14.0.jar"
    "file:///c:/maven/repository/org/apache/logging/log4j/log4j-core/2.14.0/log4j-core-2.14.0.jar"
    List<Resource> orderedJars = context.getMetaData().getOrderedWebInfJars();

    //there is an ordering, but there are no jars resulting from the ordering, everything excluded
    if (orderedJars.isEmpty())
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Excluded {} empty ordering", sci);
        return true;
    }

    //Check if it is excluded by an ordering
    URI loadingJarURI = sciResource.getURI();
    boolean found = false;
    Iterator<Resource> itor = orderedJars.iterator();
    while (!found && itor.hasNext())
    {
        Resource r = itor.next();
        found = r.getURI().equals(loadingJarURI);
    }

    if (LOG.isDebugEnabled())
        LOG.debug("{}Excluded {} found={}", found ? "!" : "", sci, found);
    return !found;

The comparison:
r.getURI().equals(loadingJarURI);

fails as the URIs:

  • "file:///c:/maven/repository/org/apache/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar" and
  • "file:///C:/maven/repository/org/apache/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar"

are not equal despite them being logically equivalent.

The drive letter case mismatch between orderedJars and sciResource occurs because Maven passes the dependency file paths with lowercase drive letters whereas AnnotationConfiguration.getJarFor(ServletContainerInitializer service) returns URIs with uppercase drive letters.

In my project, org.eclipse.jetty.maven.plugin.JettyWebAppContext.setWebInfLib(List<File> jars) receives a list of files derived from project.getArtifacts():

"c:\maven\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar"
"c:\maven\repository\org\apache\logging\log4j\log4j-web\2.14.0\log4j-web-2.14.0.jar"
"c:\maven\repository\org\apache\logging\log4j\log4j-api\2.14.0\log4j-api-2.14.0.jar"
"c:\maven\repository\org\apache\logging\log4j\log4j-core\2.14.0\log4j-core-2.14.0.jar"

The corresponding URI returned (as a PathResource) by AnnotationConfiguration.getJarFor(ServletContainerInitializer service) for Log4jServletContainerInitializer is
file:///C:/work/ext/maven/repository/org/apache/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar

The relevant log output is shown below:

> mvn -X -e jetty:run

[DEBUG] Starting Jetty Server ...
[DEBUG] Logging to org.slf4j.impl.MavenSimpleLogger(org.eclipse.jetty.util.log) via org.eclipse.jetty.util.log.Slf4jLog
[INFO] Logging initialized @2884ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO] Context path = /
[INFO] Tmp directory = C:\work\openvpms\dev\jetty-test\target\tmp
[INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[DEBUG] Artifact org.slf4j:slf4j-api:jar:1.7.25 loaded from c:\maven\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar added to WEB-INF/lib
[DEBUG] Artifact org.apache.logging.log4j:log4j-web:jar:2.14.0 loaded from c:\maven\repository\org\apache\logging\log4j\log4j-web\2.14.0\log4j-web-2.14.0.jar added to WEB-INF/lib
[DEBUG] Artifact org.apache.logging.log4j:log4j-api:jar:2.14.0 loaded from c:\maven\repository\org\apache\logging\log4j\log4j-api\2.14.0\log4j-api-2.14.0.jar added to WEB-INF/lib
[DEBUG] Artifact org.apache.logging.log4j:log4j-core:jar:2.14.0 loaded from c:\maven\repository\org\apache\logging\log4j\log4j-core\2.14.0\log4j-core-2.14.0.jar added to WEB-INF/lib
[DEBUG] WEB-INF/lib initialized (at root)
[INFO] web.xml file = file:///C://jetty-test/src/main/webapp/WEB-INF/web.xml
[INFO] Webapp directory = C:\work\openvpms\dev\jetty-test\src\main\webapp
[DEBUG] ->[{o.e.j.m.p.JettyWebAppContext@f1a45f8{/,file:///C://jetty-test/src/main/webapp/,STOPPED}{file:///C://jetty-test/src/main/webapp/},[o.e.j.m.p.JettyWebAppContext@f1a45f8{/,file:///C://jetty-test/src/main/webapp/,STOPPED}{file:///C://jetty-test/src/main/webapp/}]}]
[DEBUG] ContextHandlerCollection@6b667cb3{STOPPED} added {o.e.j.m.p.JettyWebAppContext@f1a45f8{/,file:///C://jetty-test/src/main/webapp/,STOPPED}{file:///C://jetty-test/src/main/webapp/},AUTO}
[DEBUG] starting Server@6fd77352{STOPPED}[9.4.35.v20201120]

<snip>

[DEBUG]  add  resource to resources to examine c:\maven\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar
[DEBUG]  add  resource to resources to examine *c:\maven\repository\org\apache\logging\log4j\log4j-web\2.14.0\log4j-web-2.14.0.jar*
[DEBUG]  add  resource to resources to examine c:\maven\repository\org\apache\logging\log4j\log4j-api\2.14.0\log4j-api-2.14.0.jar
[DEBUG]  add  resource to resources to examine c:\maven\repository\org\apache\logging\log4j\log4j-core\2.14.0\log4j-core-2.14.0.jar

<snip>

[DEBUG] getResources META-INF/services/javax.servlet.ServletContainerInitializer [jar:file:/C:/maven/repository/org/apache/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar!/META-INF/services/javax.servlet.ServletContainerInitializer, jar:file:/c:/maven/repository/org/eclipse/jetty/websocket/javax-websocket-server-impl/9.4.35.v20201120/javax-websocket-server-impl-9.4.35.v20201120.jar!/META-INF/services/javax.servlet.ServletContainerInitializer, jar:file:/c:/maven/repository/org/eclipse/jetty/websocket/websocket-server/9.4.35.v20201120/websocket-server-9.4.35.v20201120.jar!/META-INF/services/javax.servlet.ServletContainerInitializer, jar:file:/c:/maven/repository/org/eclipse/jetty/apache-jsp/9.4.35.v20201120/apache-jsp-9.4.35.v20201120.jar!/META-INF/services/javax.servlet.ServletContainerInitializer]
[DEBUG] isSystemResource==false org.apache.logging.log4j.web.Log4jServletContainerInitializer jar:file:/C:/maven/repository/org/apache/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar!/org/apache/logging/log4j/web/Log4jServletContainerInitializer.class
[DEBUG] isServerClass==false interface javax.servlet.ServletContainerInitializer
[DEBUG] WAP parent loaded interface javax.servlet.ServletContainerInitializer
[DEBUG] isServerClass==false class java.lang.Object
[DEBUG] WAP parent loaded class java.lang.Object
[DEBUG] WAP webapp loaded class org.apache.logging.log4j.web.Log4jServletContainerInitializer

<snip>

[DEBUG] Excluded org.apache.logging.log4j.web.Log4jServletContainerInitializer@571a9686 found=false
[DEBUG] org.apache.logging.log4j.web.Log4jServletContainerInitializer@571a9686 is from excluded jar

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.github.unb</groupId>
    <artifactId>jetty-test</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-web</artifactId>
            <version>2.14.0</version>
        </dependency>
    </dependencies>
    <properties>
        <jetty.version>9.4.35.v20201120</jetty.version>
    </properties>

    <build>
        <defaultGoal>jetty:run</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>${jetty.version}</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

web.xml

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1" metadata-complete="false">

    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>AppServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

AppServlet.java

public class AppServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        PrintWriter writer = resp.getWriter();
        ClassLoader classLoader = getClass().getClassLoader();
        if (classLoader instanceof URLClassLoader) {
            for (URL url : ((URLClassLoader) classLoader).getURLs()) {
                writer.println(url);
            }
        }
    }
}
@janbartel janbartel self-assigned this Jan 9, 2021
@unb
Copy link
Author

unb commented Jan 10, 2021

Passing maven dependency paths via getCanonicalFile() generates the correct URI e.g.:

new File("c:\\maven\\repository\\org\\apche\\logging\\log4j\\log4j-web\\2.14.0\\log4j-web-2.14.0.jar").getCanonicalFile().toPath().toUri()

produces: file:///C:/maven/repository/org/apche/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar

(c.f. new File("c:\\maven\\repository\\org\\apche\\logging\\log4j\\log4j-web\\2.14.0\\log4j-web-2.14.0.jar").getCanonicalFile().toURI()) --> file:/C:/maven/repository/org/apche/logging/log4j/log4j-web/2.14.0/log4j-web-2.14.0.jar
As far as URI is concerned, both are the same)

@gregw gregw added this to To do in Jetty 9.4.36 via automation Jan 11, 2021
janbartel added a commit that referenced this issue Jan 11, 2021
Signed-off-by: Jan Bartel <janb@webtide.com>
janbartel added a commit that referenced this issue Jan 11, 2021
Add a test for Resource.equals

Signed-off-by: Jan Bartel <janb@webtide.com>
@joakime joakime added Bug For general bugs on Jetty side Microsoft Windows For issues unique to Microsoft Windows labels Jan 11, 2021
joakime added a commit that referenced this issue Jan 11, 2021
+ Eliminating OS.MAC (as it doesn't support drive letters)
+ Adding alt URI syntax version as well
janbartel added a commit that referenced this issue Jan 12, 2021
* Issue #5870 Windows URI case comparison fails

Signed-off-by: Jan Bartel <janb@webtide.com>

* Issue #5870 - Updating Windows tests

+ Eliminating OS.MAC (as it doesn't support drive letters)
+ Adding alt URI syntax version as well

Co-authored-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
@janbartel
Copy link
Contributor

Fixed by #5873.

Jetty 9.4.36 automation moved this from To do to Done Jan 12, 2021
This was referenced Mar 10, 2021
This was referenced Mar 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side Microsoft Windows For issues unique to Microsoft Windows
Projects
No open projects
Development

No branches or pull requests

3 participants