From b7f03a2cd2cfd017ec313a1170fc55b6b7dcbef7 Mon Sep 17 00:00:00 2001
From: dsuiu <dsuiu@modernsystems.com>
Date: Sat, 27 Nov 2021 22:00:59 +0200
Subject: Add support for reading plugin descriptor from zip

---
 .../org/pf4j/ManifestPluginDescriptorFinder.java   | 76 +++++++++++++---------
 .../org/pf4j/PropertiesPluginDescriptorFinder.java | 16 ++---
 pf4j/src/main/java/org/pf4j/util/FileUtils.java    | 12 +++-
 3 files changed, 64 insertions(+), 40 deletions(-)

diff --git a/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java
index 650b2d7..7425b9d 100644
--- a/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java
+++ b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java
@@ -27,6 +27,8 @@ import java.nio.file.Path;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
 /**
  * Read the plugin descriptor from the manifest file.
@@ -48,7 +50,7 @@ public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder {
 
     @Override
     public boolean isApplicable(Path pluginPath) {
-        return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isJarFile(pluginPath));
+        return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isZipOrJarFile(pluginPath));
     }
 
     @Override
@@ -60,40 +62,14 @@ public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder {
 
     protected Manifest readManifest(Path pluginPath) {
         if (FileUtils.isJarFile(pluginPath)) {
-            try (JarFile jar = new JarFile(pluginPath.toFile())) {
-                Manifest manifest = jar.getManifest();
-                if (manifest != null) {
-                    return manifest;
-                }
-            } catch (IOException e) {
-                throw new PluginRuntimeException(e);
-            }
-        }
-
-        Path manifestPath = getManifestPath(pluginPath);
-        if (manifestPath == null) {
-            throw new PluginRuntimeException("Cannot find the manifest path");
-        }
-
-        log.debug("Lookup plugin descriptor in '{}'", manifestPath);
-        if (Files.notExists(manifestPath)) {
-            throw new PluginRuntimeException("Cannot find '{}' path", manifestPath);
+            return readManifestFromJar(pluginPath);
         }
 
-        try (InputStream input = Files.newInputStream(manifestPath)) {
-            return new Manifest(input);
-        } catch (IOException e) {
-            throw new PluginRuntimeException(e);
-        }
-    }
-
-    protected Path getManifestPath(Path pluginPath) {
-        if (Files.isDirectory(pluginPath)) {
-            // legacy (the path is something like "classes/META-INF/MANIFEST.MF")
-            return FileUtils.findFile(pluginPath,"MANIFEST.MF");
+        if (FileUtils.isZipFile(pluginPath)) {
+            return readManifestFromZip(pluginPath);
         }
 
-        return null;
+        return readManifestFromDirectory(pluginPath);
     }
 
     protected PluginDescriptor createPluginDescriptor(Manifest manifest) {
@@ -140,4 +116,42 @@ public class ManifestPluginDescriptorFinder implements PluginDescriptorFinder {
         return new DefaultPluginDescriptor();
     }
 
+    protected Manifest readManifestFromJar(Path jarPath) {
+        try (JarFile jar = new JarFile(jarPath.toFile())) {
+            return jar.getManifest();
+        } catch (IOException e) {
+            throw new PluginRuntimeException(e, "Cannot read manifest from {}", jarPath);
+        }
+    }
+
+    protected Manifest readManifestFromZip(Path zipPath) {
+        try (ZipFile zip = new ZipFile(zipPath.toFile())) {
+            ZipEntry manifestEntry = zip.getEntry("classes/META-INF/MANIFEST.MF");
+            try (InputStream manifestInput = zip.getInputStream(manifestEntry)) {
+                return new Manifest(manifestInput);
+            }
+        } catch (IOException e) {
+            throw new PluginRuntimeException(e, "Cannot read manifest from {}", zipPath);
+        }
+    }
+
+    protected Manifest readManifestFromDirectory(Path pluginPath) {
+        // legacy (the path is something like "classes/META-INF/MANIFEST.MF")
+        Path manifestPath = FileUtils.findFile(pluginPath,"MANIFEST.MF");
+        if (manifestPath == null) {
+            throw new PluginRuntimeException("Cannot find the manifest path");
+        }
+
+        log.debug("Lookup plugin descriptor in '{}'", manifestPath);
+        if (Files.notExists(manifestPath)) {
+            throw new PluginRuntimeException("Cannot find '{}' path", manifestPath);
+        }
+
+        try (InputStream input = Files.newInputStream(manifestPath)) {
+            return new Manifest(input);
+        } catch (IOException e) {
+            throw new PluginRuntimeException(e, "Cannot read manifest from {}", pluginPath);
+        }
+    }
+
 }
diff --git a/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java
index 303b662..a04d268 100644
--- a/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java
+++ b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java
@@ -59,7 +59,7 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder
 
     @Override
     public boolean isApplicable(Path pluginPath) {
-        return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isJarFile(pluginPath));
+        return Files.exists(pluginPath) && (Files.isDirectory(pluginPath) || FileUtils.isZipOrJarFile(pluginPath));
     }
 
     @Override
@@ -97,13 +97,13 @@ public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder
     protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) {
         if (Files.isDirectory(pluginPath)) {
             return pluginPath.resolve(Paths.get(propertiesFileName));
-        } else {
-            // it's a jar file
-            try {
-                return FileUtils.getPath(pluginPath, propertiesFileName);
-            } catch (IOException e) {
-                throw new PluginRuntimeException(e);
-            }
+        }
+
+        // it's a zip or jar file
+        try {
+            return FileUtils.getPath(pluginPath, propertiesFileName);
+        } catch (IOException e) {
+            throw new PluginRuntimeException(e);
         }
     }
 
diff --git a/pf4j/src/main/java/org/pf4j/util/FileUtils.java b/pf4j/src/main/java/org/pf4j/util/FileUtils.java
index 0edcf97..285a280 100644
--- a/pf4j/src/main/java/org/pf4j/util/FileUtils.java
+++ b/pf4j/src/main/java/org/pf4j/util/FileUtils.java
@@ -217,9 +217,19 @@ public final class FileUtils {
         return Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".jar");
     }
 
+    /**
+     * Return true only if path is a jar or zip file.
+     *
+     * @param path to a file/dir
+     * @return true if file ending in {@code .zip} or {@code .jar}
+     */
+    public static boolean isZipOrJarFile(Path path) {
+        return isZipFile(path) || isJarFile(path);
+    }
+
     public static Path getPath(Path path, String first, String... more) throws IOException {
         URI uri = path.toUri();
-        if (isJarFile(path)) {
+        if (isZipOrJarFile(path)) {
             String pathString = path.toAbsolutePath().toString();
             // transformation for Windows OS
             pathString = StringUtils.addStart(pathString.replace("\\", "/"), "/");
-- 
cgit v1.2.3