We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Not able to load resources when using pants vs sbt
**Describe the bug**
When using sbt we are able to call `Thread.currentThread().getContextClassLoader().getResources` and get a list of URLs. When using pants the list is empty. 

This at the moment limits us from using Flyway with pants.

**Pants version**
2.13.0a1 and main.

**OS**
MacOS

**Additional info**
Example repo to reproduce the issue:
https://github.com/somdoron/test-pants-resources

I think the issue is, that pants only compress files in the resources zip file and not the directories.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/python/pants/jvm/resources.py`
Content:
```
1 # Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2 # Licensed under the Apache License, Version 2.0 (see LICENSE).
3 
4 import itertools
5 import logging
6 from itertools import chain
7 
8 from pants.core.target_types import ResourcesFieldSet, ResourcesGeneratorFieldSet
9 from pants.core.util_rules import stripped_source_files
10 from pants.core.util_rules.source_files import SourceFilesRequest
11 from pants.core.util_rules.stripped_source_files import StrippedSourceFiles
12 from pants.core.util_rules.system_binaries import ZipBinary
13 from pants.engine.fs import Digest, MergeDigests
14 from pants.engine.internals.selectors import MultiGet
15 from pants.engine.process import Process, ProcessResult
16 from pants.engine.rules import Get, collect_rules, rule
17 from pants.engine.target import SourcesField
18 from pants.engine.unions import UnionRule
19 from pants.jvm import compile
20 from pants.jvm.compile import (
21     ClasspathDependenciesRequest,
22     ClasspathEntry,
23     ClasspathEntryRequest,
24     ClasspathEntryRequests,
25     CompileResult,
26     FallibleClasspathEntries,
27     FallibleClasspathEntry,
28 )
29 from pants.jvm.strip_jar.strip_jar import StripJarRequest
30 from pants.jvm.subsystems import JvmSubsystem
31 from pants.util.logging import LogLevel
32 
33 logger = logging.getLogger(__name__)
34 
35 
36 class JvmResourcesRequest(ClasspathEntryRequest):
37     field_sets = (
38         ResourcesFieldSet,
39         ResourcesGeneratorFieldSet,
40     )
41 
42 
43 @rule(desc="Assemble resources")
44 async def assemble_resources_jar(
45     zip: ZipBinary,
46     jvm: JvmSubsystem,
47     request: JvmResourcesRequest,
48 ) -> FallibleClasspathEntry:
49     # Request the component's direct dependency classpath, and additionally any prerequisite.
50     # Filter out any dependencies that are generated by our current target so that each resource
51     # only appears in a single input JAR.
52     # NOTE: Generated dependencies will have the same dependencies as the current target, so we
53     # don't need to inspect those dependencies.
54     optional_prereq_request = [*((request.prerequisite,) if request.prerequisite else ())]
55     fallibles = await MultiGet(
56         Get(FallibleClasspathEntries, ClasspathEntryRequests(optional_prereq_request)),
57         Get(FallibleClasspathEntries, ClasspathDependenciesRequest(request, ignore_generated=True)),
58     )
59     direct_dependency_classpath_entries = FallibleClasspathEntries(
60         itertools.chain(*fallibles)
61     ).if_all_succeeded()
62 
63     if direct_dependency_classpath_entries is None:
64         return FallibleClasspathEntry(
65             description=str(request.component),
66             result=CompileResult.DEPENDENCY_FAILED,
67             output=None,
68             exit_code=1,
69         )
70 
71     source_files = await Get(
72         StrippedSourceFiles,
73         SourceFilesRequest([tgt.get(SourcesField) for tgt in request.component.members]),
74     )
75 
76     output_filename = f"{request.component.representative.address.path_safe_spec}.resources.jar"
77     output_files = [output_filename]
78 
79     resources_jar_input_digest = source_files.snapshot.digest
80     resources_jar_result = await Get(
81         ProcessResult,
82         Process(
83             argv=[
84                 zip.path,
85                 output_filename,
86                 *source_files.snapshot.files,
87             ],
88             description="Build resources JAR for {request.component}",
89             input_digest=resources_jar_input_digest,
90             output_files=output_files,
91             level=LogLevel.DEBUG,
92         ),
93     )
94 
95     output_digest = resources_jar_result.output_digest
96     if jvm.reproducible_jars:
97         output_digest = await Get(Digest, StripJarRequest(output_digest, tuple(output_files)))
98     cpe = ClasspathEntry(output_digest, output_files, [])
99 
100     merged_cpe_digest = await Get(
101         Digest,
102         MergeDigests(chain((cpe.digest,), (i.digest for i in direct_dependency_classpath_entries))),
103     )
104 
105     merged_cpe = ClasspathEntry.merge(
106         digest=merged_cpe_digest, entries=[cpe, *direct_dependency_classpath_entries]
107     )
108 
109     return FallibleClasspathEntry(output_filename, CompileResult.SUCCEEDED, merged_cpe, 0)
110 
111 
112 def rules():
113     return [
114         *collect_rules(),
115         *compile.rules(),
116         *stripped_source_files.rules(),
117         UnionRule(ClasspathEntryRequest, JvmResourcesRequest),
118     ]
119
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/python/pants/jvm/resources.py b/src/python/pants/jvm/resources.py
--- a/src/python/pants/jvm/resources.py
+++ b/src/python/pants/jvm/resources.py
@@ -4,6 +4,7 @@
 import itertools
 import logging
 from itertools import chain
+from pathlib import Path
 
 from pants.core.target_types import ResourcesFieldSet, ResourcesGeneratorFieldSet
 from pants.core.util_rules import stripped_source_files
@@ -76,6 +77,13 @@
     output_filename = f"{request.component.representative.address.path_safe_spec}.resources.jar"
     output_files = [output_filename]
 
+    # #16231: Valid JAR files need the directories of each resource file as well as the files
+    # themselves.
+
+    paths = {Path(filename) for filename in source_files.snapshot.files}
+    directories = {parent for path in paths for parent in path.parents}
+    input_files = {str(path) for path in chain(paths, directories)}
+
     resources_jar_input_digest = source_files.snapshot.digest
     resources_jar_result = await Get(
         ProcessResult,
@@ -83,7 +91,7 @@
             argv=[
                 zip.path,
                 output_filename,
-                *source_files.snapshot.files,
+                *sorted(input_files),
             ],
             description="Build resources JAR for {request.component}",
             input_digest=resources_jar_input_digest,

{"golden_diff": "diff --git a/src/python/pants/jvm/resources.py b/src/python/pants/jvm/resources.py\n--- a/src/python/pants/jvm/resources.py\n+++ b/src/python/pants/jvm/resources.py\n@@ -4,6 +4,7 @@\n import itertools\n import logging\n from itertools import chain\n+from pathlib import Path\n \n from pants.core.target_types import ResourcesFieldSet, ResourcesGeneratorFieldSet\n from pants.core.util_rules import stripped_source_files\n@@ -76,6 +77,13 @@\n     output_filename = f\"{request.component.representative.address.path_safe_spec}.resources.jar\"\n     output_files = [output_filename]\n \n+    # #16231: Valid JAR files need the directories of each resource file as well as the files\n+    # themselves.\n+\n+    paths = {Path(filename) for filename in source_files.snapshot.files}\n+    directories = {parent for path in paths for parent in path.parents}\n+    input_files = {str(path) for path in chain(paths, directories)}\n+\n     resources_jar_input_digest = source_files.snapshot.digest\n     resources_jar_result = await Get(\n         ProcessResult,\n@@ -83,7 +91,7 @@\n             argv=[\n                 zip.path,\n                 output_filename,\n-                *source_files.snapshot.files,\n+                *sorted(input_files),\n             ],\n             description=\"Build resources JAR for {request.component}\",\n             input_digest=resources_jar_input_digest,\n", "issue": "Not able to load resources when using pants vs sbt\n**Describe the bug**\r\nWhen using sbt we are able to call `Thread.currentThread().getContextClassLoader().getResources` and get a list of URLs. When using pants the list is empty. \r\n\r\nThis at the moment limits us from using Flyway with pants.\r\n\r\n**Pants version**\r\n2.13.0a1 and main.\r\n\r\n**OS**\r\nMacOS\r\n\r\n**Additional info**\r\nExample repo to reproduce the issue:\r\nhttps://github.com/somdoron/test-pants-resources\r\n\r\nI think the issue is, that pants only compress files in the resources zip file and not the directories.\n", "before_files": [{"content": "# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nimport itertools\nimport logging\nfrom itertools import chain\n\nfrom pants.core.target_types import ResourcesFieldSet, ResourcesGeneratorFieldSet\nfrom pants.core.util_rules import stripped_source_files\nfrom pants.core.util_rules.source_files import SourceFilesRequest\nfrom pants.core.util_rules.stripped_source_files import StrippedSourceFiles\nfrom pants.core.util_rules.system_binaries import ZipBinary\nfrom pants.engine.fs import Digest, MergeDigests\nfrom pants.engine.internals.selectors import MultiGet\nfrom pants.engine.process import Process, ProcessResult\nfrom pants.engine.rules import Get, collect_rules, rule\nfrom pants.engine.target import SourcesField\nfrom pants.engine.unions import UnionRule\nfrom pants.jvm import compile\nfrom pants.jvm.compile import (\n    ClasspathDependenciesRequest,\n    ClasspathEntry,\n    ClasspathEntryRequest,\n    ClasspathEntryRequests,\n    CompileResult,\n    FallibleClasspathEntries,\n    FallibleClasspathEntry,\n)\nfrom pants.jvm.strip_jar.strip_jar import StripJarRequest\nfrom pants.jvm.subsystems import JvmSubsystem\nfrom pants.util.logging import LogLevel\n\nlogger = logging.getLogger(__name__)\n\n\nclass JvmResourcesRequest(ClasspathEntryRequest):\n    field_sets = (\n        ResourcesFieldSet,\n        ResourcesGeneratorFieldSet,\n    )\n\n\n@rule(desc=\"Assemble resources\")\nasync def assemble_resources_jar(\n    zip: ZipBinary,\n    jvm: JvmSubsystem,\n    request: JvmResourcesRequest,\n) -> FallibleClasspathEntry:\n    # Request the component's direct dependency classpath, and additionally any prerequisite.\n    # Filter out any dependencies that are generated by our current target so that each resource\n    # only appears in a single input JAR.\n    # NOTE: Generated dependencies will have the same dependencies as the current target, so we\n    # don't need to inspect those dependencies.\n    optional_prereq_request = [*((request.prerequisite,) if request.prerequisite else ())]\n    fallibles = await MultiGet(\n        Get(FallibleClasspathEntries, ClasspathEntryRequests(optional_prereq_request)),\n        Get(FallibleClasspathEntries, ClasspathDependenciesRequest(request, ignore_generated=True)),\n    )\n    direct_dependency_classpath_entries = FallibleClasspathEntries(\n        itertools.chain(*fallibles)\n    ).if_all_succeeded()\n\n    if direct_dependency_classpath_entries is None:\n        return FallibleClasspathEntry(\n            description=str(request.component),\n            result=CompileResult.DEPENDENCY_FAILED,\n            output=None,\n            exit_code=1,\n        )\n\n    source_files = await Get(\n        StrippedSourceFiles,\n        SourceFilesRequest([tgt.get(SourcesField) for tgt in request.component.members]),\n    )\n\n    output_filename = f\"{request.component.representative.address.path_safe_spec}.resources.jar\"\n    output_files = [output_filename]\n\n    resources_jar_input_digest = source_files.snapshot.digest\n    resources_jar_result = await Get(\n        ProcessResult,\n        Process(\n            argv=[\n                zip.path,\n                output_filename,\n                *source_files.snapshot.files,\n            ],\n            description=\"Build resources JAR for {request.component}\",\n            input_digest=resources_jar_input_digest,\n            output_files=output_files,\n            level=LogLevel.DEBUG,\n        ),\n    )\n\n    output_digest = resources_jar_result.output_digest\n    if jvm.reproducible_jars:\n        output_digest = await Get(Digest, StripJarRequest(output_digest, tuple(output_files)))\n    cpe = ClasspathEntry(output_digest, output_files, [])\n\n    merged_cpe_digest = await Get(\n        Digest,\n        MergeDigests(chain((cpe.digest,), (i.digest for i in direct_dependency_classpath_entries))),\n    )\n\n    merged_cpe = ClasspathEntry.merge(\n        digest=merged_cpe_digest, entries=[cpe, *direct_dependency_classpath_entries]\n    )\n\n    return FallibleClasspathEntry(output_filename, CompileResult.SUCCEEDED, merged_cpe, 0)\n\n\ndef rules():\n    return [\n        *collect_rules(),\n        *compile.rules(),\n        *stripped_source_files.rules(),\n        UnionRule(ClasspathEntryRequest, JvmResourcesRequest),\n    ]\n", "path": "src/python/pants/jvm/resources.py"}], "after_files": [{"content": "# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).\n# Licensed under the Apache License, Version 2.0 (see LICENSE).\n\nimport itertools\nimport logging\nfrom itertools import chain\nfrom pathlib import Path\n\nfrom pants.core.target_types import ResourcesFieldSet, ResourcesGeneratorFieldSet\nfrom pants.core.util_rules import stripped_source_files\nfrom pants.core.util_rules.source_files import SourceFilesRequest\nfrom pants.core.util_rules.stripped_source_files import StrippedSourceFiles\nfrom pants.core.util_rules.system_binaries import ZipBinary\nfrom pants.engine.fs import Digest, MergeDigests\nfrom pants.engine.internals.selectors import MultiGet\nfrom pants.engine.process import Process, ProcessResult\nfrom pants.engine.rules import Get, collect_rules, rule\nfrom pants.engine.target import SourcesField\nfrom pants.engine.unions import UnionRule\nfrom pants.jvm import compile\nfrom pants.jvm.compile import (\n    ClasspathDependenciesRequest,\n    ClasspathEntry,\n    ClasspathEntryRequest,\n    ClasspathEntryRequests,\n    CompileResult,\n    FallibleClasspathEntries,\n    FallibleClasspathEntry,\n)\nfrom pants.jvm.strip_jar.strip_jar import StripJarRequest\nfrom pants.jvm.subsystems import JvmSubsystem\nfrom pants.util.logging import LogLevel\n\nlogger = logging.getLogger(__name__)\n\n\nclass JvmResourcesRequest(ClasspathEntryRequest):\n    field_sets = (\n        ResourcesFieldSet,\n        ResourcesGeneratorFieldSet,\n    )\n\n\n@rule(desc=\"Assemble resources\")\nasync def assemble_resources_jar(\n    zip: ZipBinary,\n    jvm: JvmSubsystem,\n    request: JvmResourcesRequest,\n) -> FallibleClasspathEntry:\n    # Request the component's direct dependency classpath, and additionally any prerequisite.\n    # Filter out any dependencies that are generated by our current target so that each resource\n    # only appears in a single input JAR.\n    # NOTE: Generated dependencies will have the same dependencies as the current target, so we\n    # don't need to inspect those dependencies.\n    optional_prereq_request = [*((request.prerequisite,) if request.prerequisite else ())]\n    fallibles = await MultiGet(\n        Get(FallibleClasspathEntries, ClasspathEntryRequests(optional_prereq_request)),\n        Get(FallibleClasspathEntries, ClasspathDependenciesRequest(request, ignore_generated=True)),\n    )\n    direct_dependency_classpath_entries = FallibleClasspathEntries(\n        itertools.chain(*fallibles)\n    ).if_all_succeeded()\n\n    if direct_dependency_classpath_entries is None:\n        return FallibleClasspathEntry(\n            description=str(request.component),\n            result=CompileResult.DEPENDENCY_FAILED,\n            output=None,\n            exit_code=1,\n        )\n\n    source_files = await Get(\n        StrippedSourceFiles,\n        SourceFilesRequest([tgt.get(SourcesField) for tgt in request.component.members]),\n    )\n\n    output_filename = f\"{request.component.representative.address.path_safe_spec}.resources.jar\"\n    output_files = [output_filename]\n\n    # #16231: Valid JAR files need the directories of each resource file as well as the files\n    # themselves.\n\n    paths = {Path(filename) for filename in source_files.snapshot.files}\n    directories = {parent for path in paths for parent in path.parents}\n    input_files = {str(path) for path in chain(paths, directories)}\n\n    resources_jar_input_digest = source_files.snapshot.digest\n    resources_jar_result = await Get(\n        ProcessResult,\n        Process(\n            argv=[\n                zip.path,\n                output_filename,\n                *sorted(input_files),\n            ],\n            description=\"Build resources JAR for {request.component}\",\n            input_digest=resources_jar_input_digest,\n            output_files=output_files,\n            level=LogLevel.DEBUG,\n        ),\n    )\n\n    output_digest = resources_jar_result.output_digest\n    if jvm.reproducible_jars:\n        output_digest = await Get(Digest, StripJarRequest(output_digest, tuple(output_files)))\n    cpe = ClasspathEntry(output_digest, output_files, [])\n\n    merged_cpe_digest = await Get(\n        Digest,\n        MergeDigests(chain((cpe.digest,), (i.digest for i in direct_dependency_classpath_entries))),\n    )\n\n    merged_cpe = ClasspathEntry.merge(\n        digest=merged_cpe_digest, entries=[cpe, *direct_dependency_classpath_entries]\n    )\n\n    return FallibleClasspathEntry(output_filename, CompileResult.SUCCEEDED, merged_cpe, 0)\n\n\ndef rules():\n    return [\n        *collect_rules(),\n        *compile.rules(),\n        *stripped_source_files.rules(),\n        UnionRule(ClasspathEntryRequest, JvmResourcesRequest),\n    ]\n", "path": "src/python/pants/jvm/resources.py"}]}