This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/main by this push:
     new a853a2fc6 Add support for nested jars
a853a2fc6 is described below

commit a853a2fc6d06ad393071b3df48bbfc8d9a01ebec
Author: Steve Lawrence <[email protected]>
AuthorDate: Tue Apr 23 09:14:51 2024 -0400

    Add support for nested jars
    
    Daffodil supports resolving schemaLocations relative to a jar URI, which
    only looks for the schemaLocation in that same jar. Such jar URI's
    usually look like this:
    
      jar:file:/path/to/some.jar!/path/to/schema.dfdl.xsd
    
    To resolve a path relative to this, we split this into its components: a
    jar part and the resource path part inside that jar and resolve the
    schemaLocation relative to the path part.
    
    Although Java does not support loading resources within nested jars,
    tools like Spring Boot have added such support. This can result in jar
    URIs that look like this:
    
      jar:file:/path/to/outer.jar!/path/to/inner.jar!/path/to/schema.dfdl.xsd
    
    This breaks our current code because we expect there to only be two
    components, but we end up with three because of the multiple exclamation
    points.
    
    To fix this and to support nested jars, this changes the logic to only
    split on the last exclamation point. Everything before that is
    considered the jar component and everything after is considered the path
    component in that jar, which could be a nested jar. We then resolve
    relative paths to this path component as usual and look for the result
    to be in that same jar component.
    
    DAFFODIL-2892
---
 .../scala/org/apache/daffodil/lib/util/Misc.scala  | 39 ++++++++++++++++------
 1 file changed, 29 insertions(+), 10 deletions(-)

diff --git 
a/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala 
b/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala
index 2e78cefbb..50ab159ef 100644
--- a/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala
+++ b/daffodil-lib/src/main/scala/org/apache/daffodil/lib/util/Misc.scala
@@ -159,6 +159,26 @@ object Misc {
     }
   }
 
+  /**
+   * Split a jar URI into its two component parts: 1) the path to a jar file 
2) the resource
+   * path within that jar file. By definition, these two parts are delimited 
by the last
+   * occurrence of an exclamation point. If the resource path part contains an 
exclamation point
+   * it should have already been escaped using "%21", which is the URI escape 
code for an
+   * exclamation point.
+   *
+   * Note that it is important that we look for the last index of an 
exclamation point, since it
+   * is possible some tools have added support for nested jars, and could have 
multiple
+   * exclamation points for each nesting.
+   */
+  def splitJarUri(uri: URI): (String, String) = {
+    Assert.invariant(uri.getScheme == "jar")
+    var uriStr = uri.toString
+    val exclamIdx = uriStr.lastIndexOf("!")
+    val jarPart = uriStr.substring(0, exclamIdx)
+    val pathPart = uriStr.substring(exclamIdx + 1)
+    (jarPart, pathPart)
+  }
+
   /**
    * Java 20 deprecated the 2-arg URL constructor which worked to create 
relative URIs
    * within the same Jar file.
@@ -172,23 +192,19 @@ object Misc {
    *
    *    `jar:file:/..absolute path to jar file.jar!/absolute path from root 
inside jar to file``
    *
-   * We split at the !/, make a relative path on just the inside-jar-file 
part, then glue
-   * back together.
-   *
+   * We split this URI into its component parts, make a relative path on just 
the resource path
+   * inside the jar, then glue back together.
    *
    * @param contextURI
    * @param relPath
    * @return Some(uri) for an existing relative path within the same jar file, 
or None if it does not exist.
    */
   def optRelativeJarFileURI(contextURI: URI, relPath: String): Option[URI] = {
-    val parts = contextURI.toString.split("\\!\\/")
-    Assert.invariant(parts.length == 2)
-    val jarPart = parts(0)
-    val pathPart = parts(1)
-    Assert.invariant(pathPart ne null)
+    val (jarPart, pathPart) = Misc.splitJarUri(contextURI)
+    Assert.invariant(pathPart.startsWith("/"))
     val contextURIPathOnly = URI.create(pathPart)
     val resolvedURIPathOnly = contextURIPathOnly.resolve(relPath)
-    val newJarPathURI = URI.create(jarPart + "!/" + 
resolvedURIPathOnly.toString)
+    val newJarPathURI = URI.create(jarPart + "!" + 
resolvedURIPathOnly.toString)
     try {
       newJarPathURI.toURL.openStream().close()
       // that worked, so we can open it so it exists.
@@ -711,7 +727,10 @@ object Misc {
    */
   def uriToDiagnosticFile(uri: URI): File = {
     uri.getScheme match {
-      case "jar" => Paths.get(uri.toString.split("\\.jar!").last).toFile
+      case "jar" => {
+        val (_, pathPart) = Misc.splitJarUri(uri)
+        Paths.get(pathPart).toFile
+      }
       case "file" => Paths.get(uri).toFile
       case _ => Paths.get(uri.getPath).toFile
     }

Reply via email to