Repository: quarkusio/quarkus
Issue: Gradle plugin does not copy all files correctly, when having parent-first-artifacts

### Describe the bug

I've a Quarkus project, which uses an own Jackson ObjectMapper, with that I had problems using the same ClassLoader for that lib. As a solution I added the lib to the property ```quarkus.class-loading.parent-first-artifacts```.
With Quarkus < 3.x this worked well. Now I've tried to migrate to Quarkus 3.x and I got a ClassNotFoundException.

After searching I found the reason. My lib - added via the parent-first-artifact - is copied during the build to ```build/quarkus-build/gen/querkus-app/lib/boot```, which is as expected. 
But in the next step, I would expect, that it should also be copied to ```build/quarkus-build/dep/lib/boot``` (and finally to ```build/quarkus-app/lib/boot```), but this doesn't happen. The lib is mentioned in the manifest file.



### Expected behavior

The parent-first-artifact lib is copied in the app folder.

### Actual behavior

JAR is not copied.

### How to Reproduce?

Run ```gradle build``` and the ```gson``` lib (just used as an example) was not copied to ```build/quarkus-build/dep/lib/boot```

[reproducer.zip](https://github.com/quarkusio/quarkus/files/11676041/reproducer.zip)


### Output of `uname -a` or `ver`

Darwin TMXQT47KNH 22.5.0 Darwin Kernel Version 22.5.0

### Output of `java -version`

openjdk version "17.0.6" 2023-01-17 LTS

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

3.1.0

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Gradle 8.1.1

### Additional information

_No response_

Changed files:
devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java

diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java
index 2e7b714ff72..a410e1af412 100644
--- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java
+++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/tasks/QuarkusBuildDependencies.java
@@ -5,6 +5,7 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -34,6 +35,8 @@
 public abstract class QuarkusBuildDependencies extends QuarkusBuildTask {
     static final String CLASS_LOADING_REMOVED_ARTIFACTS = "quarkus.class-loading.removed-artifacts";
     static final String CLASS_LOADING_PARENT_FIRST_ARTIFACTS = "quarkus.class-loading.parent-first-artifacts";
+    static final String FILTER_OPTIONAL_DEPENDENCIES = "quarkus.package.filter-optional-dependencies";
+    static final String INCLUDED_OPTIONAL_DEPENDENCIES = "quarkus.package.included-optional-dependencies";
 
     @Inject
     public QuarkusBuildDependencies() {
@@ -139,19 +142,22 @@ private void jarDependencies(Path libBoot, Path libMain) {
         Map<String, String> configMap = extension().buildEffectiveConfiguration(appModel.getAppArtifact()).configMap();
 
         // see https://quarkus.io/guides/class-loading-reference#configuring-class-loading
-        String removedArtifactsProp = configMap.getOrDefault(CLASS_LOADING_PARENT_FIRST_ARTIFACTS, "");
-        java.util.Optional<Set<ArtifactKey>> optionalDependencies = java.util.Optional.ofNullable(
+        Set<ArtifactKey> removedArtifacts = java.util.Optional.ofNullable(
                 configMap.getOrDefault(CLASS_LOADING_REMOVED_ARTIFACTS, null))
-                .map(s -> Arrays.stream(s.split(","))
-                        .map(String::trim)
-                        .filter(gact -> !gact.isEmpty())
-                        .map(ArtifactKey::fromString)
-                        .collect(Collectors.toSet()));
-        Set<ArtifactKey> removedArtifacts = Arrays.stream(removedArtifactsProp.split(","))
-                .map(String::trim)
-                .filter(gact -> !gact.isEmpty())
-                .map(ArtifactKey::fromString)
-                .collect(Collectors.toSet());
+                .map(QuarkusBuildDependencies::dependenciesListToArtifactKeySet)
+                .orElse(Collections.emptySet());
+        getLogger().info("Removed artifacts: {}", configMap.getOrDefault(CLASS_LOADING_REMOVED_ARTIFACTS, "(none)"));
+
+        String parentFirstArtifactsProp = configMap.getOrDefault(CLASS_LOADING_PARENT_FIRST_ARTIFACTS, "");
+        Set<ArtifactKey> parentFirstArtifacts = dependenciesListToArtifactKeySet(parentFirstArtifactsProp);
+        getLogger().info("parent first artifacts: {}", configMap.getOrDefault(CLASS_LOADING_PARENT_FIRST_ARTIFACTS, "(none)"));
+
+        String optionalDependenciesProp = configMap.getOrDefault(INCLUDED_OPTIONAL_DEPENDENCIES, "");
+        boolean filterOptionalDependencies = Boolean
+                .parseBoolean(configMap.getOrDefault(FILTER_OPTIONAL_DEPENDENCIES, "false"));
+        Set<ArtifactKey> optionalDependencies = filterOptionalDependencies
+                ? dependenciesListToArtifactKeySet(optionalDependenciesProp)
+                : Collections.emptySet();
 
         appModel.getRuntimeDependencies().stream()
                 .filter(appDep -> {
@@ -159,13 +165,13 @@ private void jarDependencies(Path libBoot, Path libMain) {
                     if (!appDep.isJar()) {
                         return false;
                     }
-                    if (appDep.isOptional()) {
-                        return optionalDependencies.map(appArtifactKeys -> appArtifactKeys.contains(appDep.getKey()))
-                                .orElse(true);
+                    if (filterOptionalDependencies && appDep.isOptional()) {
+                        return optionalDependencies.contains(appDep.getKey());
                     }
                     return !removedArtifacts.contains(appDep.getKey());
                 })
-                .map(dep -> Map.entry(dep.isFlagSet(DependencyFlags.CLASSLOADER_RUNNER_PARENT_FIRST) ? libBoot : libMain, dep))
+                .map(dep -> Map.entry(dep.isFlagSet(DependencyFlags.CLASSLOADER_RUNNER_PARENT_FIRST)
+                        || parentFirstArtifacts.contains(dep.getKey()) ? libBoot : libMain, dep))
                 .peek(depAndTarget -> {
                     ResolvedDependency dep = depAndTarget.getValue();
                     Path targetDir = depAndTarget.getKey();
@@ -199,4 +205,12 @@ private void jarDependencies(Path libBoot, Path libMain) {
                 .collect(Collectors.toMap(Map.Entry::getKey, depAndTarget -> 1, Integer::sum))
                 .forEach((path, count) -> getLogger().info("Copied {} files into {}", count, path));
     }
+
+    private static Set<ArtifactKey> dependenciesListToArtifactKeySet(String optionalDependenciesProp) {
+        return Arrays.stream(optionalDependenciesProp.split(","))
+                .map(String::trim)
+                .filter(gact -> !gact.isEmpty())
+                .map(ArtifactKey::fromString)
+                .collect(Collectors.toSet());
+    }
 }