Build a compile-time classpath from the zips being patched
diff --git a/tomee-patch-plugin/src/main/java/org/apache/tomee/patch/plugin/PatchMojo.java b/tomee-patch-plugin/src/main/java/org/apache/tomee/patch/plugin/PatchMojo.java
index a237085..ef995d4 100644
--- a/tomee-patch-plugin/src/main/java/org/apache/tomee/patch/plugin/PatchMojo.java
+++ b/tomee-patch-plugin/src/main/java/org/apache/tomee/patch/plugin/PatchMojo.java
@@ -31,12 +31,14 @@
 import org.apache.maven.project.MavenProjectHelper;
 import org.apache.maven.toolchain.Toolchain;
 import org.apache.maven.toolchain.ToolchainManager;
+import org.apache.tomee.patch.core.Is;
 import org.codehaus.plexus.compiler.Compiler;
 import org.codehaus.plexus.compiler.CompilerConfiguration;
 import org.codehaus.plexus.compiler.CompilerMessage;
 import org.codehaus.plexus.compiler.CompilerResult;
 import org.codehaus.plexus.compiler.manager.CompilerManager;
 import org.codehaus.plexus.compiler.manager.NoSuchCompilerException;
+import org.tomitribe.jkta.usage.Dir;
 import org.tomitribe.util.Files;
 import org.tomitribe.util.Zips;
 
@@ -48,6 +50,8 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -63,10 +67,16 @@
     @Parameter(defaultValue = "${project.build.directory}", required = true)
     private File outputDirectory;
 
-    @Parameter(defaultValue = "${project.basedir}/src/main/patch", required = true)
+    @Parameter(defaultValue = "${project.basedir}/src/patch/java", required = true)
     private File patchSourceDirectory;
 
     /**
+     * Regex to identify which archives should be matched
+     */
+    @Parameter(defaultValue = "jakartaee9.*\\.zip", required = true)
+    private String select;
+
+    /**
      * <p>
      * Specify the requirements for this jdk toolchain.
      * This overrules the toolchain selected by the maven-toolchain-plugin.
@@ -99,10 +109,17 @@
     /**
      * The target directory of the compiler if fork is true.
      */
-    @Parameter(defaultValue = "${project.build.directory}", required = true, readonly = true)
+    @Parameter(defaultValue = "${project.build.directory}/patch-classes", required = true, readonly = true)
     private File buildDirectory;
 
     /**
+     * The directory where we will extract the zips being patched so we can compile the
+     * patch source against the jars contained within.
+     */
+    @Parameter(defaultValue = "${project.build.directory}/patch-classpath", required = true)
+    private File patchClasspathDirectory;
+
+    /**
      * The -encoding argument for the Java compiler.
      *
      * @since 2.1
@@ -153,137 +170,195 @@
      */
     public void execute() throws MojoExecutionException, CompilationFailureException {
         try {
-            final List<Artifact> artifacts = Stream.of(getSourceArtifacts())
-                    .filter(artifact -> artifact.getFile().getName().contains("jakartaee9"))
-                    .collect(Collectors.toList());
+            Files.mkdir(patchClasspathDirectory);
+            
+            // Select the zip files and jars we'll be potentially patching
+            final List<Artifact> artifacts = getPatchArtifacts();
 
-            final File archives = new File(outputDirectory, "patch");
-            Files.mkdir(archives);
+            // Extract any zips and return a list of jars
+            final List<File> jars = prepareJars(artifacts);
 
-            Compiler compiler;
+            compile(patchSourceDirectory, jars);
 
-            getLog().debug("Using compiler '" + compilerId + "'.");
-
-            try {
-                compiler = compilerManager.getCompiler(compilerId);
-            } catch (NoSuchCompilerException e) {
-                throw new MojoExecutionException("No such compiler '" + e.getCompilerId() + "'.");
-            }
-
-            Toolchain tc = getToolchain();
-            if (tc != null) {
-                getLog().info("Toolchain in maven-compiler-plugin: " + tc);
-                //TODO somehow shaky dependency between compilerId and tool executable.
-                executable = tc.findTool(compilerId);
-            }
-
-            final CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
-            compilerConfiguration.setOutputLocation(outputDirectory.getAbsolutePath());
-            compilerConfiguration.setOptimize(false);
-            compilerConfiguration.setDebug(true);
-            compilerConfiguration.setParameters(false);
-            compilerConfiguration.setVerbose(false);
-            compilerConfiguration.setShowWarnings(false);
-            compilerConfiguration.setFailOnWarning(false);
-            compilerConfiguration.setShowDeprecation(false);
-            compilerConfiguration.setSourceVersion(source);
-            compilerConfiguration.setTargetVersion(target);
-            compilerConfiguration.setReleaseVersion(null);
-            compilerConfiguration.setProc(null);
-            compilerConfiguration.setSourceLocations(getPatchSourceLocations());
-            compilerConfiguration.setAnnotationProcessors(null);
-            compilerConfiguration.setSourceEncoding(encoding);
-            compilerConfiguration.setFork(true);
-            compilerConfiguration.setExecutable(executable);
-            compilerConfiguration.setWorkingDirectory(basedir);
-            compilerConfiguration.setCompilerVersion(compilerVersion);
-            compilerConfiguration.setBuildDirectory(buildDirectory);
-            compilerConfiguration.setOutputFileName(null);
-
-            final CompilerResult compilerResult;
-            try {
-                compilerResult = compiler.performCompile(compilerConfiguration);
-            } catch (Exception e) {
-                throw new MojoExecutionException("Fatal error compiling", e);
-            }
-
-            List<CompilerMessage> warnings = new ArrayList<>();
-            List<CompilerMessage> errors = new ArrayList<>();
-            List<CompilerMessage> others = new ArrayList<>();
-            for (CompilerMessage message : compilerResult.getCompilerMessages()) {
-                if (message.getKind() == CompilerMessage.Kind.ERROR) {
-                    errors.add(message);
-                } else if (message.getKind() == CompilerMessage.Kind.WARNING
-                        || message.getKind() == CompilerMessage.Kind.MANDATORY_WARNING) {
-                    warnings.add(message);
-                } else {
-                    others.add(message);
-                }
-            }
-
-            if (true && !compilerResult.isSuccess()) {
-                for (CompilerMessage message : others) {
-                    assert message.getKind() != CompilerMessage.Kind.ERROR
-                            && message.getKind() != CompilerMessage.Kind.WARNING
-                            && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
-                    getLog().info(message.toString());
-                }
-                if (!warnings.isEmpty()) {
-                    getLog().info("-------------------------------------------------------------");
-                    getLog().warn("COMPILATION WARNING : ");
-                    getLog().info("-------------------------------------------------------------");
-                    for (CompilerMessage warning : warnings) {
-                        getLog().warn(warning.toString());
-                    }
-                    getLog().info(warnings.size() + ((warnings.size() > 1) ? " warnings " : " warning"));
-                    getLog().info("-------------------------------------------------------------");
-                }
-
-                if (!errors.isEmpty()) {
-                    getLog().info("-------------------------------------------------------------");
-                    getLog().error("COMPILATION ERROR : ");
-                    getLog().info("-------------------------------------------------------------");
-                    for (CompilerMessage error : errors) {
-                        getLog().error(error.toString());
-                    }
-                    getLog().info(errors.size() + ((errors.size() > 1) ? " errors " : " error"));
-                    getLog().info("-------------------------------------------------------------");
-                }
-
-                if (!errors.isEmpty()) {
-                    throw new CompilationFailureException(errors);
-                } else {
-                    throw new CompilationFailureException(warnings);
-                }
-            } else {
-                for (CompilerMessage message : compilerResult.getCompilerMessages()) {
-                    switch (message.getKind()) {
-                        case NOTE:
-                        case OTHER:
-                            getLog().info(message.toString());
-                            break;
-
-                        case ERROR:
-                            getLog().error(message.toString());
-                            break;
-
-                        case MANDATORY_WARNING:
-                        case WARNING:
-                        default:
-                            getLog().warn(message.toString());
-                            break;
-                    }
-                }
-            }
-
-            for (final Artifact artifact : artifacts) {
-                Zips.unzip(artifact.getFile(), archives);
-            }
         } catch (IOException e) {
             throw new MojoExecutionException("Error occurred during execution", e);
         }
     }
 
+    /**
+     * Any zip files contained in the Artifact set should be extracted
+     * Any jar files contained in the Artifact set will be returned as-is
+     */
+    private List<File> prepareJars(final List<Artifact> artifacts) throws IOException {
+
+        // Extract all zip, war, ear, rar files.  Do not extract jar files.
+        for (final Artifact artifact : artifacts) {
+            if (isZip(artifact.getFile()) && !isJar(artifact.getFile())) {
+                Zips.unzip(artifact.getFile(), patchClasspathDirectory);
+            }
+        }
+
+        // Collect a list of jars
+        final List<File> jars = new ArrayList<>();
+
+        // Add any artifacts that are already jars
+        artifacts.stream()
+                .map(Artifact::getFile)
+                .filter(File::isFile)
+                .filter(this::isJar)
+                .forEach(jars::add);
+
+        // Add any extracted files that are jars
+        Dir.from(patchClasspathDirectory)
+                .files()
+                .filter(File::isFile)
+                .filter(this::isJar)
+                .forEach(jars::add);
+
+        return jars;
+    }
+
+    private boolean isJar(final File file) {
+        return file.getName().endsWith(".jar");
+    }
+
+    private static boolean isZip(final File file) {
+        return new Is.Zip().accept(file);
+    }
+
+    private List<Artifact> getPatchArtifacts() {
+        final Predicate<String> match = Pattern.compile(select).asPredicate();
+        return Stream.of(getSourceArtifacts())
+                .filter(artifact -> match.test(artifact.getFile().getName()))
+                .collect(Collectors.toList());
+    }
+
+    private void compile(final File patchSourceDirectory, final List<File> jars) throws MojoExecutionException, CompilationFailureException {
+
+        getLog().debug("Using compiler '" + compilerId + "'.");
+
+        final Compiler compiler;
+
+        try {
+            compiler = compilerManager.getCompiler(compilerId);
+        } catch (NoSuchCompilerException e) {
+            throw new MojoExecutionException("No such compiler '" + e.getCompilerId() + "'.");
+        }
+
+        final Toolchain tc = getToolchain();
+        if (tc != null) {
+            getLog().info("Toolchain in maven-compiler-plugin: " + tc);
+            //TODO somehow shaky dependency between compilerId and tool executable.
+            executable = tc.findTool(compilerId);
+        }
+
+
+        final CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
+        compilerConfiguration.setOutputLocation(buildDirectory.getAbsolutePath());
+        compilerConfiguration.setOptimize(false);
+        compilerConfiguration.setDebug(true);
+        compilerConfiguration.setParameters(false);
+        compilerConfiguration.setVerbose(false);
+        compilerConfiguration.setShowWarnings(false);
+        compilerConfiguration.setFailOnWarning(false);
+        compilerConfiguration.setShowDeprecation(false);
+        compilerConfiguration.setSourceVersion(source);
+        compilerConfiguration.setTargetVersion(target);
+        compilerConfiguration.setReleaseVersion(null);
+        compilerConfiguration.setProc(null);
+        compilerConfiguration.setSourceLocations(getPatchSourceLocations());
+        compilerConfiguration.setAnnotationProcessors(null);
+        compilerConfiguration.setSourceEncoding(encoding);
+        compilerConfiguration.setFork(true);
+        compilerConfiguration.setExecutable(executable);
+        compilerConfiguration.setWorkingDirectory(basedir);
+        compilerConfiguration.setCompilerVersion(compilerVersion);
+        compilerConfiguration.setBuildDirectory(buildDirectory);
+        compilerConfiguration.setOutputFileName(null);
+
+        // Add each jar as a classpath entry
+        jars.stream()
+                .map(File::getAbsolutePath)
+                .forEach(compilerConfiguration::addClasspathEntry);
+
+        // Now we can compile!
+        final CompilerResult compilerResult;
+        try {
+            compilerResult = compiler.performCompile(compilerConfiguration);
+        } catch (Exception e) {
+            throw new MojoExecutionException("Fatal error compiling", e);
+        }
+
+        List<CompilerMessage> warnings = new ArrayList<>();
+        List<CompilerMessage> errors = new ArrayList<>();
+        List<CompilerMessage> others = new ArrayList<>();
+        for (CompilerMessage message : compilerResult.getCompilerMessages()) {
+            if (message.getKind() == CompilerMessage.Kind.ERROR) {
+                errors.add(message);
+            } else if (message.getKind() == CompilerMessage.Kind.WARNING
+                    || message.getKind() == CompilerMessage.Kind.MANDATORY_WARNING) {
+                warnings.add(message);
+            } else {
+                others.add(message);
+            }
+        }
+
+        if (!compilerResult.isSuccess()) {
+            for (CompilerMessage message : others) {
+                assert message.getKind() != CompilerMessage.Kind.ERROR
+                        && message.getKind() != CompilerMessage.Kind.WARNING
+                        && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
+                getLog().info(message.toString());
+            }
+            if (!warnings.isEmpty()) {
+                getLog().info("-------------------------------------------------------------");
+                getLog().warn("COMPILATION WARNING : ");
+                getLog().info("-------------------------------------------------------------");
+                for (CompilerMessage warning : warnings) {
+                    getLog().warn(warning.toString());
+                }
+                getLog().info(warnings.size() + ((warnings.size() > 1) ? " warnings " : " warning"));
+                getLog().info("-------------------------------------------------------------");
+            }
+
+            if (!errors.isEmpty()) {
+                getLog().info("-------------------------------------------------------------");
+                getLog().error("COMPILATION ERROR : ");
+                getLog().info("-------------------------------------------------------------");
+                for (CompilerMessage error : errors) {
+                    getLog().error(error.toString());
+                }
+                getLog().info(errors.size() + ((errors.size() > 1) ? " errors " : " error"));
+                getLog().info("-------------------------------------------------------------");
+            }
+
+            if (!errors.isEmpty()) {
+                throw new CompilationFailureException(errors);
+            } else {
+                throw new CompilationFailureException(warnings);
+            }
+        } else {
+            for (CompilerMessage message : compilerResult.getCompilerMessages()) {
+                switch (message.getKind()) {
+                    case NOTE:
+                    case OTHER:
+                        getLog().info(message.toString());
+                        break;
+
+                    case ERROR:
+                        getLog().error(message.toString());
+                        break;
+
+                    case MANDATORY_WARNING:
+                    case WARNING:
+                    default:
+                        getLog().warn(message.toString());
+                        break;
+                }
+            }
+        }
+    }
+
     private List<String> getPatchSourceLocations() {
         return Collections.singletonList(patchSourceDirectory.getAbsolutePath());
     }