Skip to content

Commit

Permalink
Automatically download plugin dependencies from repositories (Fix pf4…
Browse files Browse the repository at this point in the history
  • Loading branch information
DRSchlaubi committed Nov 21, 2021
1 parent f49a8e5 commit aef0b8a
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 14 deletions.
26 changes: 26 additions & 0 deletions src/main/java/org/pf4j/update/PluginInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public static class PluginRelease implements Serializable {
public Date date;
public String requires;
public String url;
/**
* A list of {@link PluginSpec plugin specs} this plugin depends on.
*/
public List<PluginSpec> dependencies;

/**
* Optional sha512 digest checksum. Can be one of
Expand All @@ -88,9 +92,31 @@ public String toString() {
", requires='" + requires + '\'' +
", url='" + url + '\'' +
", sha512sum='" + sha512sum + '\'' +
", dependencies='" + dependencies + '\'' +
'}';
}

}

/**
* Specification of a specific plugin version.
*/
public static class PluginSpec implements Serializable {

private final String pluginId;
private final String version;

public PluginSpec(String pluginId, String version) {
this.pluginId = pluginId;
this.version = version;
}

public String getPluginId() {
return pluginId;
}

public String getVersion() {
return version;
}
}
}
119 changes: 105 additions & 14 deletions src/main/java/org/pf4j/update/UpdateManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.pf4j.DependencyResolver;
import org.pf4j.PluginManager;
import org.pf4j.PluginRuntimeException;
import org.pf4j.PluginState;
Expand Down Expand Up @@ -173,7 +174,7 @@ public void setRepositories(List<UpdateRepository> repositories) {
/**
* Add one {@link DefaultUpdateRepository}.
*
* @param id of repo
* @param id of repo
* @param url of repo
*/
public void addRepository(String id, URL url) {
Expand Down Expand Up @@ -206,13 +207,13 @@ public void addRepository(UpdateRepository newRepo) {
* @param id of repository to remove
*/
public void removeRepository(String id) {
for (UpdateRepository repo : getRepositories()) {
if (id.equals(repo.getId())) {
repositories.remove(repo);
break;
for (UpdateRepository repo : getRepositories()) {
if (id.equals(repo.getId())) {
repositories.remove(repo);
break;
}
}
}
log.warn("Repository with id " + id + " not found, doing nothing");
log.warn("Repository with id " + id + " not found, doing nothing");
}

/**
Expand All @@ -228,15 +229,73 @@ public synchronized void refresh() {
lastPluginRelease.clear();
}

protected void installPluginDependencies(String id, String version) {
installPluginDependencies(id, version, Collections.singletonList(id));
}

protected void installPluginDependencies(String id, String version, List<String> dependencyPath) {
PluginInfo plugin = getPluginsMap().get(id);
PluginRelease release = plugin.releases
.stream()
.filter(it -> it.version.equals(version))
.findFirst()
.orElseThrow(() ->
new IllegalArgumentException(
String.format("Could not find version %s for plugin %s", id, version)));
List<PluginInfo.PluginSpec> dependencies = release.dependencies;

dependencies.forEach(dependency -> {
PluginWrapper installedPlugin = pluginManager.getPlugin(dependency.getPluginId());

if (dependencyPath.contains(dependency.getPluginId())) {
String dependencyId = dependency.getPluginId();
throw new PluginRuntimeException("Found circular dependency: %s",
buildDependencyPathString(dependencyPath, dependencyId));
}

List<String> newDependencyPath = new ArrayList<>(dependencyPath);
newDependencyPath.add(dependency.getPluginId());
installPluginDependencies(id, version, newDependencyPath);

if (installedPlugin == null) {
installPlugin(dependency.getPluginId(), dependency.getVersion(), false);
} else if (!installedPlugin.getDescriptor().getVersion().equals(dependency.getVersion())) {
updatePlugin(dependency.getPluginId(), dependency.getVersion(), false);
}
});

}

/**
* Installs a plugin by id and version.
*
* @param id the id of plugin to install
* @param id the id of plugin to install
* @param version the version of plugin to install, on SemVer format, or null for latest
* @return true if installation successful and plugin started
* @exception PluginRuntimeException if plugin does not exist in repos or problems during
* @throws PluginRuntimeException if plugin does not exist in repos or problems during
*/
public synchronized boolean installPlugin(String id, String version) {
return installPlugin(id, version, true);
}

/**
* Installs a plugin by id and version.
*
* @param id the id of plugin to install
* @param version the version of plugin to install, on SemVer format, or null for latest
* @param installDependencies whether to install dependencies or not.
* This wil also update existing plugins, if the requested plugin
* requires a new version of the other installed plugin.
* This won't check if the update, conflicts with any other
* installed plugins
* Check {@link PluginInfo.PluginRelease#dependencies} for more information
* @return true if installation successful and plugin started
* @throws PluginRuntimeException if plugin does not exist in repos or problems during
*/
public synchronized boolean installPlugin(String id, String version, boolean installDependencies) {
if (installDependencies) {
installPluginDependencies(id, version);
}
// Download to temporary location
Path downloaded = downloadPlugin(id, version);

Expand All @@ -258,7 +317,7 @@ public synchronized boolean installPlugin(String id, String version) {
* Downloads a plugin with given coordinates, runs all {@link FileVerifier}s
* and returns a path to the downloaded file.
*
* @param id of plugin
* @param id of plugin
* @param version of plugin or null to download latest
* @return Path to file which will reside in a temporary folder in the system default temp area
* @throws PluginRuntimeException if download failed
Expand Down Expand Up @@ -310,7 +369,7 @@ protected FileVerifier getFileVerifier(String pluginId) {
/**
* Resolves Release from id and version.
*
* @param id of plugin
* @param id of plugin
* @param version of plugin or null to locate latest version
* @return PluginRelease for downloading
* @throws PluginRuntimeException if id or version does not exist
Expand Down Expand Up @@ -338,12 +397,31 @@ protected PluginRelease findReleaseForPlugin(String id, String version) {
/**
* Updates a plugin id to given version or to latest version if {@code version == null}.
*
* @param id the id of plugin to update
* @param id the id of plugin to update
* @param version the version to update to, on SemVer format, or null for latest
* @return true if update successful
* @exception PluginRuntimeException in case the given version is not available, plugin id not already installed etc
*/
* @throws PluginRuntimeException in case the given version is not available, plugin id not already installed etc
*/
public boolean updatePlugin(String id, String version) {
return updatePlugin(id, version, true);
}

/**
* Updates a plugin id to given version or to latest version if {@code version == null}.
*
* @param id the id of plugin to update
* @param version the version to update to, on SemVer format, or null for latest
* @param updateDependencies whether to update the dependency plugins of the plugin as well.
* This also installs new dependencies the previous plugin version
* did not have
* Check {@link PluginInfo.PluginRelease#dependencies} for more information
* @return true if update successful
* @throws PluginRuntimeException in case the given version is not available, plugin id not already installed etc
*/
public boolean updatePlugin(String id, String version, boolean updateDependencies) {
if (updateDependencies) {
installPluginDependencies(id, version);
}
if (pluginManager.getPlugin(id) == null) {
throw new PluginRuntimeException("Plugin {} cannot be updated since it is not installed", id);
}
Expand Down Expand Up @@ -438,4 +516,17 @@ protected synchronized void initRepositoriesFromJson() {
}
}


protected String buildDependencyPathString(List<String> dependencyPath, String dependency) {
List<String> path = new ArrayList<>(dependencyPath);
path.add(dependency);
StringBuilder builder = new StringBuilder();

path.forEach(item -> {
builder.append(item);
builder.append(" -> ");
});

return builder.toString();
}
}

0 comments on commit aef0b8a

Please sign in to comment.