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 12.x startup fails because MetaInfConfiguration is throwing an exception while parsing java classpath with URIUtil when classpath contains wildcard ./mypath/* #11092

Closed
jdgenerix opened this issue Dec 21, 2023 · 7 comments · Fixed by #12287
Assignees
Labels
Bug For general bugs on Jetty side

Comments

@jdgenerix
Copy link

Jetty version(s)
12.0.4

Jetty Environment
ee10

Java version/vendor (use: java -version)
Java VM: OpenJDK 64-Bit Server VM 17.0.8.1+1, mixed mode, sharing, Eclipse Adoptium

OS type/version
Windows 10 Enterprise

Description
org.eclipse.jetty.ee10.webapp.MetaInfConfiguration is calling URIUtil::toURI with values from the classpath. If the classpath contains wildcard values like .\mypath* it fails with an java.nio.file.InvalidPathException

Extract from MetaInfConfiguration :

    String classPath = System.getProperty("java.class.path");
    if (classPath != null)
    {
        Stream.of(classPath.split(File.pathSeparator))
            .map(URIUtil::toURI)
            .filter(uriPatternPredicate)
            .forEach(addContainerResource);
    }

java.nio.file.InvalidPathException: Illegal char <*> at index xx : C:\temp\test.\mypath*
at java.base/sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182) ~[?:?]
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153) ~[?:?]
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77) ~[?:?]
at java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92) ~[?:?]
at java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:232) ~[?:?]
at java.base/java.nio.file.Path.of(Path.java:147) ~[?:?]
at java.base/java.nio.file.Paths.get(Paths.java:69) ~[?:?]
at org.eclipse.jetty.util.URIUtil.toURI(URIUtil.java:1841) ~[jetty-util-12.0.4.jar:12.0.4]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[?:?]
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[?:?]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[?:?]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[?:?]
at org.eclipse.jetty.ee10.webapp.MetaInfConfiguration.findAndFilterContainerPaths(MetaInfConfiguration.java:165) ~[jetty-ee10-webapp-12.0.4.jar:12.0.4]

How to reproduce?

import org.eclipse.jetty.util.URIUtil;

public class URIUtilTest {
public static void main(String[] args) {
URIUtil.toURI("C:\mainpath\mypath\*");
}
}

@jdgenerix jdgenerix added the Bug For general bugs on Jetty side label Dec 21, 2023
@jdgenerix jdgenerix changed the title Jetty 12.x startup fail because MetaInfConfiguration is failing while parsing java classpath with URIUtil when classpath contains wildcard ./mypath/* Jetty 12.x startup fails because MetaInfConfiguration is failing while parsing java classpath with URIUtil when classpath contains wildcard ./mypath/* Dec 21, 2023
@jdgenerix jdgenerix changed the title Jetty 12.x startup fails because MetaInfConfiguration is failing while parsing java classpath with URIUtil when classpath contains wildcard ./mypath/* Jetty 12.x startup fails because MetaInfConfiguration is throwing an exception while parsing java classpath with URIUtil when classpath contains wildcard ./mypath/* Dec 21, 2023
@joakime
Copy link
Contributor

joakime commented Dec 21, 2023

There's only 1 place a glob is supported for classpath stuff, and that's the WebAppContext and extraClasspath entries.
And we have test cases for that.

How are you configuring your WebAppContext?
Can you share your code where you call any method on WebAppContext ? (this includes any XML based configuration)

@jdgenerix
Copy link
Author

jdgenerix commented Dec 21, 2023

I can only share partial parts.

jettyWebAppContext.setWelcomeFiles(new String[0]);
jettyWebAppContext.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
jettyWebAppContext.setTempDirectoryPersistent(false);
jettyWebAppContext.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", "./classes/.");
String webInfIncludeJArPattern = ./.spring-..jar$|./.jetty-..jar$|.*/.com.corp.frmwrk..jar$
jettyWebAppContext.setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, webInfIncludeJarPattern);

In some situation
jettyWebAppContext.setDescriptor(overrideWebXML);
jettyWebAppContext.setOverrideDescriptors(overrides);

for a one context containing static files :
context.setInitParameter("org.eclipse.jetty.servlet.Default.useFileMappedBuffer", "false");
context.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
context.setInitParameter("org.eclipse.jetty.servlet.Default.acceptRanges", "true");
context.setInitParameter("org.eclipse.jetty.servlet.Default.welcomeServlets", "false");
context.setInitParameter("org.eclipse.jetty.servlet.Default.etags", "true");
context.setTempDirectoryPersistent(false);
context.setInitParameter("org.eclipse.jetty.servlet.Default.precompressed", "br=.br,gzip=.gz");

And some part of the classpath is declared this way.
java -cp "%APP_HOME%\optional-lib*;%APP_HOME%\lib*;%APP_HOME%\drivers*"

Here is a more complete stacktrace:

java.nio.file.InvalidPathException: Illegal char <*> at index 66: D:\XXXXXXXXXXXXX\YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.\optional-lib*
at java.base/sun.nio.fs.WindowsPathParser.normalize(WindowsPathParser.java:182) ~[?:?]
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:153) ~[?:?]
at java.base/sun.nio.fs.WindowsPathParser.parse(WindowsPathParser.java:77) ~[?:?]
at java.base/sun.nio.fs.WindowsPath.parse(WindowsPath.java:92) ~[?:?]
at java.base/sun.nio.fs.WindowsFileSystem.getPath(WindowsFileSystem.java:232) ~[?:?]
at java.base/java.nio.file.Path.of(Path.java:147) ~[?:?]
at java.base/java.nio.file.Paths.get(Paths.java:69) ~[?:?]
at org.eclipse.jetty.util.URIUtil.toURI(URIUtil.java:1841) ~[jetty-util-12.0.4.jar:12.0.4]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[?:?]
at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[?:?]
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[?:?]
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[?:?]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?]
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[?:?]
at org.eclipse.jetty.ee10.webapp.MetaInfConfiguration.findAndFilterContainerPaths(MetaInfConfiguration.java:165) ~[jetty-ee10-webapp-12.0.4.jar:12.0.4]
at org.eclipse.jetty.ee10.webapp.MetaInfConfiguration.preConfigure(MetaInfConfiguration.java:99) ~[jetty-ee10-webapp-12.0.4.jar:12.0.4]
at org.eclipse.jetty.ee10.webapp.Configurations.preConfigure(Configurations.java:487) ~[jetty-ee10-webapp-12.0.4.jar:12.0.4]
at org.eclipse.jetty.ee10.webapp.WebAppContext.preConfigure(WebAppContext.java:454) ~[jetty-ee10-webapp-12.0.4.jar:12.0.4]
at org.eclipse.jetty.ee10.webapp.WebAppContext.doStart(WebAppContext.java:495) [jetty-ee10-webapp-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:121) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.Handler$Abstract.doStart(Handler.java:468) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:121) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.Handler$Abstract.doStart(Handler.java:468) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:121) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.Handler$Abstract.doStart(Handler.java:468) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:121) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.Handler$Abstract.doStart(Handler.java:468) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.handler.gzip.GzipHandler.doStart(GzipHandler.java:118) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.Server.start(Server.java:622) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:121) [jetty-util-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.Handler$Abstract.doStart(Handler.java:468) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.server.Server.doStart(Server.java:563) [jetty-server-12.0.4.jar:12.0.4]
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) [jetty-util-12.0.4.jar:12.0.4]

@joakime
Copy link
Contributor

joakime commented Dec 21, 2023

Yes, this is totally within the processing of java.class.path per that stacktrace.

The thing is, when using a WebAppContext, we have to support the Servlet spec, which means scanning the various jars / classes that exist on the WebAppContext and server level.
The java.class.path is looked at to hopefully get the list of server classes.
But when you use a glob, aka C:\opt\lib\* then that hides the jars from Jetty.

In previous versions of Jetty, that entry would result in a File (or Path) object that would be invalid (it doesn't exist), and those jars would never be scanned.

We have tried in the past to mimic the behavior of the java.class.path behavior with globs, but the rules for glob are very JVM + OS dependant, so we cannot write a generic routine to resolve the same JAR files (or directories) that the JVM is using. We wind up scanning different content than what is actually loaded by the JVM.

In short, using glob on the java.class.path (aka java -cp <path>) is unsupported for the Servlet spec.
Having it error out like it is now is not 100% ideal, but it does highlight the problem.
Having it silently ignore the entries like in the past is worse.

The best short term fix for this is to make the error more clear.
Perhaps even toss up WARNING level log events when the entry is invalid / doesn't exist / etc. (with a configuration option to ignore those invalid entries)

It's been a while since we looked at this issue, I wonder if there are new APIs (or jmx details) that we can use to find the actual resolved list of JARs instead of relying on java.class.path.

@jdgenerix
Copy link
Author

If there is a way to avoid the failure, like with a warning, i would love that.

@janbartel
Copy link
Contributor

@jdgenerix have you tried this on jetty-11? Does it produce a problem?
@joakime if it works on jetty-11, then maybe we need to revert the code in ee10 to the jetty-11 handling?

@joakime
Copy link
Contributor

joakime commented Dec 22, 2023

@janbartel jetty-11 quietly drops the string entry with the glob.

It does this by taking the String, converting to File/Path, and then trying to use it, but fails (as that is an invalid File/Path).
I'll submit a PR that fails the initialization on bad entries of java.class.path with a clear error message, and then also providing a System property to ignore invalid entries.

@jdgenerix
Copy link
Author

Thanks to @janbartel for your suggestion.
Thanks to @joakime for your test.

I've used a workaround to avoid using a wildcard in the classpath, and i don't have exception anymore.
Here are my workarounds, i'm listing all the jar files from the %APP_HOME%\lib\ directory, concat their names appending either ; or : and assign the result into a MAINLIB variable.

on Windows :

cd /D "%APP_HOME%\lib\"
set MAINLIBS=
for /f "delims=" %%a in ('dir /B/S *.jar') do (
	call set MAINLIBS=%%MAINLIBS%%%%a;
)

on Linux :
MAINLIBS=$(find '$APP_HOME/lib/' -maxdepth 10 -type f -name *.jar -printf '%P:')

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
Projects
No open projects
Status: ✅ Done
3 participants