This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new dc1696b126 issue #7666: install marketplace lib/core jars (sticky on
uninstall) (#7668)
dc1696b126 is described below
commit dc1696b126a5a507899fddc3d142bdba48be34af
Author: Matt Casters <[email protected]>
AuthorDate: Wed Jul 29 08:42:57 2026 +0200
issue #7666: install marketplace lib/core jars (sticky on uninstall) (#7668)
Plugin zips place Maven provided-scope libraries under lib/core for the
shared system classpath. Marketplace was skipping those paths, so optional
plugins (e.g. Azure Key Vault SecretClient) failed after install.
Install now copies lib/core entries (with a log when replacing different
content). Uninstall still leaves lib/core jars in place so shared stacks
remain available to other plugins.
---
.../hop/marketplace/install/PluginInstaller.java | 23 +++++++++++-------
.../hop/marketplace/install/PluginUninstaller.java | 11 +++++----
.../marketplace/install/PluginInstallerTest.java | 28 +++++++++++++---------
3 files changed, 39 insertions(+), 23 deletions(-)
diff --git
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginInstaller.java
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginInstaller.java
index e8410b1c7d..9bbb7e05c7 100644
---
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginInstaller.java
+++
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginInstaller.java
@@ -224,17 +224,23 @@ public class PluginInstaller {
Path stageRoot = hopHome.resolve(STAGING_DIR).resolve(artifactId);
try {
for (String relative : relativePaths) {
- // Never overwrite core beam libraries from a marketplace zip
- if (isProtectedPath(relative)) {
- log.logBasic("Skipping protected path from marketplace package: " +
relative);
- continue;
- }
Path from = stageRoot.resolve(relative);
Path to = hopHome.resolve(relative);
if (Files.isDirectory(from)) {
Files.createDirectories(to);
} else if (Files.isRegularFile(from)) {
Files.createDirectories(to.getParent());
+ // Shared classpath jars (Maven provided → lib/core). Install them
so marketplace
+ // packages match the full client assembly; warn when replacing
different content.
+ if (isSharedCorePath(relative)
+ && Files.isRegularFile(to)
+ && Files.mismatch(from, to) != -1L) {
+ log.logBasic(
+ "Replacing shared lib/core jar with different content from "
+ + artifactId
+ + ": "
+ + relative);
+ }
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
}
@@ -244,10 +250,11 @@ public class PluginInstaller {
}
/**
- * Core install paths marketplace zips must not overwrite. Beam SDKs live
under the Beam plugin
- * ({@code plugins/engines/beam/lib-beam}) and are installed/uninstalled
with that plugin.
+ * Paths under {@code lib/core/} hold shared system-classpath jars (plugin
assembly {@code
+ * provided} scope). Marketplace <strong>installs</strong> these jars so
optional plugins work the
+ * same as the full client; uninstall never removes them (sticky shared
libraries).
*/
- static boolean isProtectedPath(String relative) {
+ static boolean isSharedCorePath(String relative) {
String normalized = relative.replace('\\', '/');
return normalized.equals("lib/core") || normalized.startsWith("lib/core/");
}
diff --git
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginUninstaller.java
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginUninstaller.java
index e4b1ddb8c4..e7d2fe31ef 100644
---
a/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginUninstaller.java
+++
b/plugins/misc/marketplace/src/main/java/org/apache/hop/marketplace/install/PluginUninstaller.java
@@ -26,7 +26,10 @@ import java.util.Set;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.logging.ILogChannel;
-/** Removes a marketplace-installed plugin using its receipt. Never touches
core-protected paths. */
+/**
+ * Removes a marketplace-installed plugin using its receipt. Shared {@code
lib/core} jars stay in
+ * place so other plugins (and the system classpath) keep working.
+ */
public class PluginUninstaller {
private final ILogChannel log;
@@ -46,11 +49,11 @@ public class PluginUninstaller {
+ "'. Only plugins installed via the marketplace can be
uninstalled this way.");
}
- // Delete files deepest-first; skip protected paths (e.g. lib/core)
+ // Delete files deepest-first; leave shared lib/core jars (sticky, may be
used by others)
Set<Path> dirs = new HashSet<>();
for (String relative : receipt.getPaths()) {
- if (PluginInstaller.isProtectedPath(relative)) {
- log.logBasic("Leaving protected path in place: " + relative);
+ if (PluginInstaller.isSharedCorePath(relative)) {
+ log.logBasic("Leaving shared lib/core path in place: " + relative);
continue;
}
Path path = hopHome.resolve(relative);
diff --git
a/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/install/PluginInstallerTest.java
b/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/install/PluginInstallerTest.java
index 112bb7cae7..0e85de4156 100644
---
a/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/install/PluginInstallerTest.java
+++
b/plugins/misc/marketplace/src/test/java/org/apache/hop/marketplace/install/PluginInstallerTest.java
@@ -49,12 +49,13 @@ class PluginInstallerTest {
}
@Test
- void protectedPaths() {
- assertFalse(PluginInstaller.isProtectedPath("lib/beam/foo.jar"));
-
assertFalse(PluginInstaller.isProtectedPath("plugins/engines/beam/lib-beam/x.jar"));
- assertTrue(PluginInstaller.isProtectedPath("lib/core/bar.jar"));
-
assertFalse(PluginInstaller.isProtectedPath("plugins/engines/beam/hop.jar"));
-
assertFalse(PluginInstaller.isProtectedPath("plugins/tech/parquet/lib/x.jar"));
+ void sharedCorePaths() {
+ assertFalse(PluginInstaller.isSharedCorePath("lib/beam/foo.jar"));
+
assertFalse(PluginInstaller.isSharedCorePath("plugins/engines/beam/lib-beam/x.jar"));
+ assertTrue(PluginInstaller.isSharedCorePath("lib/core/bar.jar"));
+ assertTrue(PluginInstaller.isSharedCorePath("lib/core"));
+
assertFalse(PluginInstaller.isSharedCorePath("plugins/engines/beam/hop.jar"));
+
assertFalse(PluginInstaller.isSharedCorePath("plugins/tech/parquet/lib/x.jar"));
}
@Test
@@ -93,14 +94,18 @@ class PluginInstallerTest {
assertTrue(
Files.isRegularFile(
hopHome.resolve(PluginInstaller.RECEIPTS_DIR).resolve("hop-test-plugin.json")));
- // protected path from zip must not be written
- assertFalse(Files.exists(hopHome.resolve("lib/core/evil.jar")));
+ // provided-scope jars land under lib/core and must be installed (system
classpath)
+ Path sharedJar = hopHome.resolve("lib/core/shared.jar");
+ assertTrue(Files.isRegularFile(sharedJar));
+ assertEquals("shared-lib", Files.readString(sharedJar));
new PluginUninstaller(log, hopHome).uninstall("hop-test-plugin");
assertFalse(Files.exists(pluginJar));
assertFalse(
Files.exists(
hopHome.resolve(PluginInstaller.RECEIPTS_DIR).resolve("hop-test-plugin.json")));
+ // lib/core is sticky: left in place so other plugins can share it
+ assertTrue(Files.isRegularFile(sharedJar));
} finally {
server.stop(0);
}
@@ -208,9 +213,10 @@ class PluginInstallerTest {
zos.putNextEntry(new ZipEntry("plugins/tech/test/version.xml"));
zos.write("<version>1.0.0</version>".getBytes(StandardCharsets.UTF_8));
zos.closeEntry();
- // Should be ignored on activate
- zos.putNextEntry(new ZipEntry("lib/core/evil.jar"));
- zos.write("nope".getBytes(StandardCharsets.UTF_8));
+ // Shared system-classpath jar (assembly provided → lib/core); must
install, sticky on
+ // uninstall
+ zos.putNextEntry(new ZipEntry("lib/core/shared.jar"));
+ zos.write("shared-lib".getBytes(StandardCharsets.UTF_8));
zos.closeEntry();
}
return bos.toByteArray();