Croway commented on code in PR #23129:
URL: https://github.com/apache/camel/pull/23129#discussion_r3225043169
##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginHelper.java:
##########
@@ -179,30 +221,247 @@ public static Map<String, Plugin>
getActivePlugins(CamelJBangMain main, String r
versionCheck(main, version, firstVersion, command);
}
- Optional<Plugin> plugin = getPlugin(command, version, gav,
repos, main.getOut());
- if (plugin.isPresent()) {
- activePlugins.put(command, plugin.get());
+ ResolveResult res = resolvePlugin(properties, command,
version, gav, repos, main.getOut());
+ if (res.plugin().isPresent()) {
+ activePlugins.put(command, res.plugin().get());
+ if (res.cacheWritten()) {
+ configDirty = true;
+ }
} else {
main.getOut().println("camel-jbang-plugin-" + command + "
not found. Exit");
main.quit(1);
}
}
+ if (configDirty) {
+ savePluginConfig(config);
+ }
}
return activePlugins;
}
public static Optional<Plugin> getPlugin(String name, String
defaultVersion, String gav, String repos, Printer printer) {
+ return resolvePlugin(null, name, defaultVersion, gav, repos,
printer).plugin();
+ }
+
+ /**
+ * Resolves a plugin by trying, in order: the cached metadata in the
plugin entry (fast path with no IO beyond
+ * size+mtime checks), the factory finder (embedded plugin on the JVM
classpath), and finally the Maven downloader.
+ * When the downloader runs, the resolved classpath is captured into the
plugin entry's {@code resolved} block so
+ * subsequent invocations take the fast path.
+ */
+ private static ResolveResult resolvePlugin(
+ JsonObject entry, String name, String defaultVersion, String gav,
String repos, Printer printer) {
+ Optional<Plugin> cached = loadFromCache(entry, defaultVersion, gav,
repos);
+ if (cached.isPresent()) {
+ return new ResolveResult(cached, false);
+ }
+
Optional<Plugin> plugin =
FACTORY_FINDER.newInstance("camel-jbang-plugin-" + name, Plugin.class);
- if (plugin.isEmpty()) {
- final MavenGav mavenGav = dependencyAsMavenGav(gav);
- final String group = extractGroup(mavenGav, "org.apache.camel");
- final String depVersion = extractVersion(mavenGav, defaultVersion);
+ if (plugin.isPresent()) {
+ return new ResolveResult(plugin, false);
+ }
+
+ final MavenGav mavenGav = dependencyAsMavenGav(gav);
+ final String group = extractGroup(mavenGav, "org.apache.camel");
+ final String depVersion = extractVersion(mavenGav, defaultVersion);
+
+ DownloadResult dr = downloadPlugin(name, defaultVersion, depVersion,
group, repos, printer);
+ boolean cacheWritten = false;
+ if (dr.plugin().isPresent() && entry != null && dr.classLoader() !=
null && dr.className() != null) {
+ cacheWritten = writeCache(entry, defaultVersion, gav, repos,
dr.className(), dr.classLoader(), name, depVersion);
+ }
+ return new ResolveResult(dr.plugin(), cacheWritten);
+ }
+
+ private static Optional<Plugin> loadFromCache(JsonObject entry, String
camelVersion, String gav, String repos) {
+ if (entry == null) {
+ return Optional.empty();
+ }
+ JsonObject resolved = entry.getMap("resolved");
+ if (resolved == null) {
+ return Optional.empty();
+ }
+ if (!sameCamelVersion(asString(resolved.get("camelVersion")),
camelVersion)) {
+ return Optional.empty();
+ }
+ if (!nullSafeEquals(normalize(asString(resolved.get("gav"))),
normalize(gav))) {
+ return Optional.empty();
+ }
+ if (!nullSafeEquals(normalize(asString(resolved.get("repos"))),
normalize(repos))) {
+ return Optional.empty();
+ }
+ String className = asString(resolved.get("className"));
+ if (className == null || className.isBlank()) {
+ return Optional.empty();
+ }
+ Object cpObj = resolved.get("classpath");
+ if (!(cpObj instanceof Collection)) {
+ return Optional.empty();
+ }
+ Collection<?> classpath = (Collection<?>) cpObj;
+ if (classpath.isEmpty()) {
+ return Optional.empty();
+ }
+
+ List<URL> urls = new ArrayList<>(classpath.size());
+ for (Object o : classpath) {
+ if (!(o instanceof Map)) {
+ return Optional.empty();
+ }
+ Map<?, ?> jar = (Map<?, ?>) o;
+ Path p = validateFileEntry(jar);
+ if (p == null) {
+ return Optional.empty();
+ }
+ try {
+ urls.add(p.toUri().toURL());
+ } catch (IOException e) {
+ return Optional.empty();
+ }
+ }
+
+ // If the cache tracks the plugin POM, validate it too. Detects
POM-only changes (e.g. a SNAPSHOT
+ // plugin's transitive deps changed without a jar rebuild).
+ Object pomObj = resolved.get("pom");
+ if (pomObj instanceof Map) {
+ if (validateFileEntry((Map<?, ?>) pomObj) == null) {
+ return Optional.empty();
+ }
+ }
+
+ try {
+ URLClassLoader cl = new URLClassLoader(urls.toArray(new URL[0]),
PluginHelper.class.getClassLoader());
+ Class<?> pluginClass = cl.loadClass(className);
+ Plugin instance = (Plugin) ObjectHelper.newInstance(pluginClass);
+ instance.setClassLoader(cl);
+ return Optional.of(instance);
+ } catch (Exception e) {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Persists the resolved plugin classpath into the entry's {@code
resolved} block. Package-private so unit tests can
+ * drive the happy path without invoking the Maven downloader. Also tracks
the plugin's own POM file (size+mtime) so
+ * a POM-only change (e.g. a SNAPSHOT plugin gaining a new transitive
dependency without a jar rebuild) invalidates
+ * the cache on the next invocation.
+ */
+ static boolean writeCache(
+ JsonObject entry, String camelVersion, String gav, String repos,
String className, ClassLoader cl,
+ String pluginCommand, String pluginVersion) {
+ URL[] urls;
+ if (cl instanceof URLClassLoader) {
Review Comment:
same
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]