} | } | ||||
@Override | @Override | ||||
public String loadPlugin(Path pluginPath) throws PluginException { | |||||
public String loadPlugin(Path pluginPath) { | |||||
if ((pluginPath == null) || Files.notExists(pluginPath)) { | if ((pluginPath == null) || Files.notExists(pluginPath)) { | ||||
throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginPath)); | throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginPath)); | ||||
} | } | ||||
for (Path pluginPath : pluginPaths) { | for (Path pluginPath : pluginPaths) { | ||||
try { | try { | ||||
loadPluginFromPath(pluginPath); | loadPluginFromPath(pluginPath); | ||||
} catch (PluginException e) { | |||||
} catch (PluginRuntimeException e) { | |||||
log.error(e.getMessage(), e); | log.error(e.getMessage(), e); | ||||
} | } | ||||
} | } | ||||
// resolve plugins | // resolve plugins | ||||
try { | try { | ||||
resolvePlugins(); | resolvePlugins(); | ||||
} catch (PluginException e) { | |||||
} catch (PluginRuntimeException e) { | |||||
log.error(e.getMessage(), e); | log.error(e.getMessage(), e); | ||||
} | } | ||||
} | } | ||||
* Unload the specified plugin and it's dependents. | * Unload the specified plugin and it's dependents. | ||||
*/ | */ | ||||
@Override | @Override | ||||
public boolean unloadPlugin(String pluginId) throws PluginException { | |||||
public boolean unloadPlugin(String pluginId) { | |||||
return unloadPlugin(pluginId, true); | return unloadPlugin(pluginId, true); | ||||
} | } | ||||
private boolean unloadPlugin(String pluginId, boolean unloadDependents) throws PluginException { | |||||
private boolean unloadPlugin(String pluginId, boolean unloadDependents) { | |||||
try { | try { | ||||
if (unloadDependents) { | if (unloadDependents) { | ||||
List<String> dependents = dependencyResolver.getDependents(pluginId); | List<String> dependents = dependencyResolver.getDependents(pluginId); | ||||
try { | try { | ||||
((Closeable) classLoader).close(); | ((Closeable) classLoader).close(); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException("Cannot close classloader", e); | |||||
throw new PluginRuntimeException(e, "Cannot close classloader"); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
@Override | @Override | ||||
public boolean deletePlugin(String pluginId) throws PluginException { | |||||
public boolean deletePlugin(String pluginId) { | |||||
checkPluginId(pluginId); | checkPluginId(pluginId); | ||||
PluginWrapper pluginWrapper = getPlugin(pluginId); | PluginWrapper pluginWrapper = getPlugin(pluginId); | ||||
* Start the specified plugin and its dependencies. | * Start the specified plugin and its dependencies. | ||||
*/ | */ | ||||
@Override | @Override | ||||
public PluginState startPlugin(String pluginId) throws PluginException { | |||||
public PluginState startPlugin(String pluginId) { | |||||
checkPluginId(pluginId); | checkPluginId(pluginId); | ||||
PluginWrapper pluginWrapper = getPlugin(pluginId); | PluginWrapper pluginWrapper = getPlugin(pluginId); | ||||
itr.remove(); | itr.remove(); | ||||
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); | ||||
} catch (PluginException e) { | |||||
} catch (PluginRuntimeException e) { | |||||
log.error(e.getMessage(), e); | log.error(e.getMessage(), e); | ||||
} | } | ||||
} | } | ||||
* Stop the specified plugin and it's dependents. | * Stop the specified plugin and it's dependents. | ||||
*/ | */ | ||||
@Override | @Override | ||||
public PluginState stopPlugin(String pluginId) throws PluginException { | |||||
public PluginState stopPlugin(String pluginId) { | |||||
return stopPlugin(pluginId, true); | return stopPlugin(pluginId, true); | ||||
} | } | ||||
private PluginState stopPlugin(String pluginId, boolean stopDependents) throws PluginException { | |||||
private PluginState stopPlugin(String pluginId, boolean stopDependents) { | |||||
checkPluginId(pluginId); | checkPluginId(pluginId); | ||||
PluginWrapper pluginWrapper = getPlugin(pluginId); | PluginWrapper pluginWrapper = getPlugin(pluginId); | ||||
} | } | ||||
@Override | @Override | ||||
public boolean disablePlugin(String pluginId) throws PluginException { | |||||
public boolean disablePlugin(String pluginId) { | |||||
checkPluginId(pluginId); | checkPluginId(pluginId); | ||||
PluginWrapper pluginWrapper = getPlugin(pluginId); | PluginWrapper pluginWrapper = getPlugin(pluginId); | ||||
} | } | ||||
@Override | @Override | ||||
public boolean enablePlugin(String pluginId) throws PluginException { | |||||
public boolean enablePlugin(String pluginId) { | |||||
checkPluginId(pluginId); | checkPluginId(pluginId); | ||||
PluginWrapper pluginWrapper = getPlugin(pluginId); | PluginWrapper pluginWrapper = getPlugin(pluginId); | ||||
for (ExtensionWrapper extensionWrapper : extensionsWrapper) { | for (ExtensionWrapper extensionWrapper : extensionsWrapper) { | ||||
try { | try { | ||||
extensions.add(extensionWrapper.getExtension()); | extensions.add(extensionWrapper.getExtension()); | ||||
} catch (PluginException e) { | |||||
} catch (PluginRuntimeException e) { | |||||
log.error("Cannot retrieve extension", e); | log.error("Cannot retrieve extension", e); | ||||
} | } | ||||
} | } | ||||
return pluginStatusProvider.isPluginDisabled(pluginId); | return pluginStatusProvider.isPluginDisabled(pluginId); | ||||
} | } | ||||
protected void resolvePlugins() throws PluginException { | |||||
protected void resolvePlugins() { | |||||
// retrieves the plugins descriptors | // retrieves the plugins descriptors | ||||
List<PluginDescriptor> descriptors = new ArrayList<>(); | List<PluginDescriptor> descriptors = new ArrayList<>(); | ||||
for (PluginWrapper plugin : plugins.values()) { | for (PluginWrapper plugin : plugins.values()) { | ||||
} | } | ||||
} | } | ||||
protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { | |||||
protected PluginWrapper loadPluginFromPath(Path pluginPath) { | |||||
// Test for plugin path duplication | // Test for plugin path duplication | ||||
String pluginId = idForPath(pluginPath); | String pluginId = idForPath(pluginPath); | ||||
if (pluginId != null) { | if (pluginId != null) { | ||||
pluginId = pluginDescriptor.getPluginId(); | pluginId = pluginDescriptor.getPluginId(); | ||||
if (plugins.containsKey(pluginId)) { | if (plugins.containsKey(pluginId)) { | ||||
PluginWrapper loadedPlugin = getPlugin(pluginId); | PluginWrapper loadedPlugin = getPlugin(pluginId); | ||||
throw new PluginException("There is an already loaded plugin ({}) " | |||||
throw new PluginRuntimeException("There is an already loaded plugin ({}) " | |||||
+ "with the same id ({}) as the plugin at path '{}'. Simultaneous loading " | + "with the same id ({}) as the plugin at path '{}'. Simultaneous loading " | ||||
+ "of plugins with the same PluginId is not currently supported.\n" | + "of plugins with the same PluginId is not currently supported.\n" | ||||
+ "As a workaround you may include PluginVersion and PluginProvider " | + "As a workaround you may include PluginVersion and PluginProvider " | ||||
* Override this to change the validation criteria. | * Override this to change the validation criteria. | ||||
* | * | ||||
* @param descriptor the plugin descriptor to validate | * @param descriptor the plugin descriptor to validate | ||||
* @throws PluginException if validation fails | |||||
* @throws PluginRuntimeException if validation fails | |||||
*/ | */ | ||||
protected void validatePluginDescriptor(PluginDescriptor descriptor) throws PluginException { | |||||
protected void validatePluginDescriptor(PluginDescriptor descriptor) { | |||||
if (StringUtils.isNullOrEmpty(descriptor.getPluginId())) { | if (StringUtils.isNullOrEmpty(descriptor.getPluginId())) { | ||||
throw new PluginException("Field 'id' cannot be empty"); | |||||
throw new PluginRuntimeException("Field 'id' cannot be empty"); | |||||
} | } | ||||
if (descriptor.getVersion() == null) { | if (descriptor.getVersion() == null) { | ||||
throw new PluginException("Field 'version' cannot be empty"); | |||||
throw new PluginRuntimeException("Field 'version' cannot be empty"); | |||||
} | } | ||||
} | } | ||||
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { | ||||
try { | try { | ||||
extensions.add(extensionWrapper.getExtension()); | extensions.add(extensionWrapper.getExtension()); | ||||
} catch (PluginException e) { | |||||
} catch (PluginRuntimeException e) { | |||||
log.error("Cannot retrieve extension", e); | log.error("Cannot retrieve extension", e); | ||||
} | } | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public boolean deletePluginPath(Path pluginPath) throws PluginException { | |||||
public boolean deletePluginPath(Path pluginPath) { | |||||
if (!filter.accept(pluginPath.toFile())) { | if (!filter.accept(pluginPath.toFile())) { | ||||
return false; | return false; | ||||
} | } | ||||
} catch (NoSuchFileException e) { | } catch (NoSuchFileException e) { | ||||
return false; // Return false on not found to be compatible with previous API (#135) | return false; // Return false on not found to be compatible with previous API (#135) | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@Override | @Override | ||||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||||
public PluginDescriptor find(Path pluginPath) { | |||||
for (PluginDescriptorFinder finder : finders) { | for (PluginDescriptorFinder finder : finders) { | ||||
if (finder.isApplicable(pluginPath)) { | if (finder.isApplicable(pluginPath)) { | ||||
log.debug("'{}' is applicable for plugin '{}'", finder, pluginPath); | log.debug("'{}' is applicable for plugin '{}'", finder, pluginPath); | ||||
} | } | ||||
} | } | ||||
throw new PluginException("No PluginDescriptorFinder for plugin '{}'", pluginPath); | |||||
throw new PluginRuntimeException("No PluginDescriptorFinder for plugin '{}'", pluginPath); | |||||
} | } | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public boolean deletePluginPath(Path pluginPath) throws PluginException { | |||||
public boolean deletePluginPath(Path pluginPath) { | |||||
for (PluginRepository repository : repositories) { | for (PluginRepository repository : repositories) { | ||||
if (repository.deletePluginPath(pluginPath)) { | if (repository.deletePluginPath(pluginPath)) { | ||||
return true; | return true; |
* Creates an extension instance. | * Creates an extension instance. | ||||
*/ | */ | ||||
@Override | @Override | ||||
public <T> T create(Class<T> extensionClass) throws PluginException { | |||||
public <T> T create(Class<T> extensionClass) { | |||||
log.debug("Create instance for extension '{}'", extensionClass.getName()); | log.debug("Create instance for extension '{}'", extensionClass.getName()); | ||||
try { | try { | ||||
return extensionClass.newInstance(); | return extensionClass.newInstance(); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Load a plugin from disk. If the path is a zip file, first unpack | |||||
* Load a plugin from disk. If the path is a zip file, first unpack. | |||||
* | |||||
* @param pluginPath plugin location on disk | * @param pluginPath plugin location on disk | ||||
* @return PluginWrapper for the loaded plugin or null if not loaded | * @return PluginWrapper for the loaded plugin or null if not loaded | ||||
* @throws PluginException if problems during load | |||||
* @throws PluginRuntimeException if problems during load | |||||
*/ | */ | ||||
@Override | @Override | ||||
protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { | |||||
protected PluginWrapper loadPluginFromPath(Path pluginPath) { | |||||
// First unzip any ZIP files | // First unzip any ZIP files | ||||
try { | try { | ||||
pluginPath = FileUtils.expandIfZip(pluginPath); | pluginPath = FileUtils.expandIfZip(pluginPath); |
import org.pf4j.util.ZipFileFilter; | import org.pf4j.util.ZipFileFilter; | ||||
import org.slf4j.Logger; | import org.slf4j.Logger; | ||||
import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||
import org.pf4j.util.NameFileFilter; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileFilter; | import java.io.FileFilter; | ||||
} | } | ||||
@Override | @Override | ||||
public boolean deletePluginPath(Path pluginPath) throws PluginException { | |||||
public boolean deletePluginPath(Path pluginPath) { | |||||
FileUtils.optimisticDelete(FileUtils.findWithEnding(pluginPath, ".zip", ".ZIP", ".Zip")); | FileUtils.optimisticDelete(FileUtils.findWithEnding(pluginPath, ".zip", ".ZIP", ".Zip")); | ||||
return super.deletePluginPath(pluginPath); | return super.deletePluginPath(pluginPath); | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public void disablePlugin(String pluginId) throws PluginException { | |||||
public void disablePlugin(String pluginId) { | |||||
disabledPlugins.add(pluginId); | disabledPlugins.add(pluginId); | ||||
try { | try { | ||||
FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
} | } | ||||
@Override | @Override | ||||
public void enablePlugin(String pluginId) throws PluginException { | |||||
public void enablePlugin(String pluginId) { | |||||
disabledPlugins.remove(pluginId); | disabledPlugins.remove(pluginId); | ||||
try { | try { | ||||
FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* It will be thrown if a cyclic dependency is detected. | * It will be thrown if a cyclic dependency is detected. | ||||
*/ | */ | ||||
public static class CyclicDependencyException extends PluginException { | |||||
public static class CyclicDependencyException extends PluginRuntimeException { | |||||
public CyclicDependencyException() { | public CyclicDependencyException() { | ||||
super("Cyclic dependencies"); | super("Cyclic dependencies"); | ||||
/** | /** | ||||
* Indicates that the dependencies required were not found. | * Indicates that the dependencies required were not found. | ||||
*/ | */ | ||||
public static class DependenciesNotFoundException extends PluginException { | |||||
public static class DependenciesNotFoundException extends PluginRuntimeException { | |||||
private List<String> dependencies; | private List<String> dependencies; | ||||
/** | /** | ||||
* Indicates that some dependencies have wrong version. | * Indicates that some dependencies have wrong version. | ||||
*/ | */ | ||||
public static class DependenciesWrongVersionException extends PluginException { | |||||
public static class DependenciesWrongVersionException extends PluginRuntimeException { | |||||
private List<WrongDependencyVersion> dependencies; | private List<WrongDependencyVersion> dependencies; | ||||
*/ | */ | ||||
public interface ExtensionFactory { | public interface ExtensionFactory { | ||||
<T> T create(Class<T> extensionClass) throws PluginException; | |||||
<T> T create(Class<T> extensionClass); | |||||
} | } |
} | } | ||||
@SuppressWarnings("unchecked") | @SuppressWarnings("unchecked") | ||||
public T getExtension() throws PluginException { | |||||
public T getExtension() { | |||||
if (extension == null) { | if (extension == null) { | ||||
extension = (T) extensionFactory.create(descriptor.extensionClass); | extension = (T) extensionFactory.create(descriptor.extensionClass); | ||||
} | } |
} | } | ||||
@Override | @Override | ||||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||||
public PluginDescriptor find(Path pluginPath) { | |||||
Manifest manifest = readManifest(pluginPath); | Manifest manifest = readManifest(pluginPath); | ||||
return createPluginDescriptor(manifest); | return createPluginDescriptor(manifest); | ||||
} | } | ||||
protected Manifest readManifest(Path pluginPath) throws PluginException { | |||||
protected Manifest readManifest(Path pluginPath) { | |||||
if (FileUtils.isJarFile(pluginPath)) { | if (FileUtils.isJarFile(pluginPath)) { | ||||
try (JarFile jar = new JarFile(pluginPath.toFile())) { | try (JarFile jar = new JarFile(pluginPath.toFile())) { | ||||
Manifest manifest = jar.getManifest(); | Manifest manifest = jar.getManifest(); | ||||
return manifest; | return manifest; | ||||
} | } | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
} | } | ||||
Path manifestPath = getManifestPath(pluginPath); | Path manifestPath = getManifestPath(pluginPath); | ||||
if (manifestPath == null) { | if (manifestPath == null) { | ||||
throw new PluginException("Cannot find the manifest path"); | |||||
throw new PluginRuntimeException("Cannot find the manifest path"); | |||||
} | } | ||||
log.debug("Lookup plugin descriptor in '{}'", manifestPath); | log.debug("Lookup plugin descriptor in '{}'", manifestPath); | ||||
if (Files.notExists(manifestPath)) { | if (Files.notExists(manifestPath)) { | ||||
throw new PluginException("Cannot find '{}' path", manifestPath); | |||||
throw new PluginRuntimeException("Cannot find '{}' path", manifestPath); | |||||
} | } | ||||
try (InputStream input = Files.newInputStream(manifestPath)) { | try (InputStream input = Files.newInputStream(manifestPath)) { | ||||
return new Manifest(input); | return new Manifest(input); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
} | } | ||||
* This method is called by the application when the plugin is started. | * This method is called by the application when the plugin is started. | ||||
* See {@link PluginManager#startPlugin(String)}. | * See {@link PluginManager#startPlugin(String)}. | ||||
*/ | */ | ||||
public void start() throws PluginException { | |||||
public void start() { | |||||
} | } | ||||
/** | /** | ||||
* This method is called by the application when the plugin is stopped. | * This method is called by the application when the plugin is stopped. | ||||
* See {@link PluginManager#stopPlugin(String)}. | * See {@link PluginManager#stopPlugin(String)}. | ||||
*/ | */ | ||||
public void stop() throws PluginException { | |||||
public void stop() { | |||||
} | } | ||||
/** | /** | ||||
* This method is called by the application when the plugin is deleted. | * This method is called by the application when the plugin is deleted. | ||||
* See {@link PluginManager#deletePlugin(String)}. | * See {@link PluginManager#deletePlugin(String)}. | ||||
*/ | */ | ||||
public void delete() throws PluginException { | |||||
public void delete() { | |||||
} | } | ||||
} | } |
/** | /** | ||||
* @author Decebal Suiu | * @author Decebal Suiu | ||||
*/ | */ | ||||
public class PluginAlreadyLoadedException extends PluginException { | |||||
public class PluginAlreadyLoadedException extends PluginRuntimeException { | |||||
private final String pluginId; | private final String pluginId; | ||||
private final Path pluginPath; | private final Path pluginPath; |
/** | /** | ||||
* Returns true if this finder is applicable to the given {@link Path}. | * Returns true if this finder is applicable to the given {@link Path}. | ||||
* | |||||
* @param pluginPath | |||||
* @return | |||||
*/ | */ | ||||
boolean isApplicable(Path pluginPath); | boolean isApplicable(Path pluginPath); | ||||
PluginDescriptor find(Path pluginPath) throws PluginException; | |||||
PluginDescriptor find(Path pluginPath); | |||||
} | } |
* Load a plugin. | * Load a plugin. | ||||
* | * | ||||
* @param pluginPath the plugin location | * @param pluginPath the plugin location | ||||
* @return the pluginId of the installed plugin as specified in | |||||
* its {@linkplain PluginDescriptor metadata} | |||||
* @throws PluginException if load of plugin fails | |||||
* @return the pluginId of the installed plugin as specified in its {@linkplain PluginDescriptor metadata} | |||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
String loadPlugin(Path pluginPath) throws PluginException; | |||||
String loadPlugin(Path pluginPath); | |||||
/** | /** | ||||
* Start all active plugins. | * Start all active plugins. | ||||
* Start the specified plugin and its dependencies. | * Start the specified plugin and its dependencies. | ||||
* | * | ||||
* @return the plugin state | * @return the plugin state | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
PluginState startPlugin(String pluginId) throws PluginException; | |||||
PluginState startPlugin(String pluginId); | |||||
/** | /** | ||||
* Stop all active plugins. | * Stop all active plugins. | ||||
* Stop the specified plugin and its dependencies. | * Stop the specified plugin and its dependencies. | ||||
* | * | ||||
* @return the plugin state | * @return the plugin state | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
PluginState stopPlugin(String pluginId) throws PluginException; | |||||
PluginState stopPlugin(String pluginId); | |||||
/** | /** | ||||
* Unload a plugin. | * Unload a plugin. | ||||
* | * | ||||
* @param pluginId the unique plugin identifier, specified in its metadata | * @param pluginId the unique plugin identifier, specified in its metadata | ||||
* @return true if the plugin was unloaded | * @return true if the plugin was unloaded | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
boolean unloadPlugin(String pluginId) throws PluginException; | |||||
boolean unloadPlugin(String pluginId); | |||||
/** | /** | ||||
* Disables a plugin from being loaded. | * Disables a plugin from being loaded. | ||||
* | * | ||||
* @param pluginId the unique plugin identifier, specified in its metadata | * @param pluginId the unique plugin identifier, specified in its metadata | ||||
* @return true if plugin is disabled | * @return true if plugin is disabled | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
boolean disablePlugin(String pluginId) throws PluginException; | |||||
boolean disablePlugin(String pluginId); | |||||
/** | /** | ||||
* Enables a plugin that has previously been disabled. | * Enables a plugin that has previously been disabled. | ||||
* | * | ||||
* @param pluginId the unique plugin identifier, specified in its metadata | * @param pluginId the unique plugin identifier, specified in its metadata | ||||
* @return true if plugin is enabled | * @return true if plugin is enabled | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
boolean enablePlugin(String pluginId) throws PluginException; | |||||
boolean enablePlugin(String pluginId); | |||||
/** | /** | ||||
* Deletes a plugin. | * Deletes a plugin. | ||||
* | * | ||||
* @param pluginId the unique plugin identifier, specified in its metadata | * @param pluginId the unique plugin identifier, specified in its metadata | ||||
* @return true if the plugin was deleted | * @return true if the plugin was deleted | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
boolean deletePlugin(String pluginId) throws PluginException; | |||||
boolean deletePlugin(String pluginId); | |||||
ClassLoader getPluginClassLoader(String pluginId); | ClassLoader getPluginClassLoader(String pluginId); | ||||
String getSystemVersion(); | String getSystemVersion(); | ||||
/** | /** | ||||
* Gets the path of the folder where plugins are installed | |||||
* Gets the path of the folder where plugins are installed. | |||||
* | |||||
* @return Path of plugins root | * @return Path of plugins root | ||||
*/ | */ | ||||
Path getPluginsRoot(); | Path getPluginsRoot(); |
* | * | ||||
* @param pluginPath the plugin path | * @param pluginPath the plugin path | ||||
* @return true if deleted | * @return true if deleted | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
boolean deletePluginPath(Path pluginPath) throws PluginException; | |||||
boolean deletePluginPath(Path pluginPath); | |||||
} | } |
* | * | ||||
* @author Decebal Suiu | * @author Decebal Suiu | ||||
*/ | */ | ||||
public class PluginException extends Exception { | |||||
public class PluginRuntimeException extends RuntimeException { | |||||
public PluginException() { | |||||
public PluginRuntimeException() { | |||||
super(); | super(); | ||||
} | } | ||||
public PluginException(String message) { | |||||
public PluginRuntimeException(String message) { | |||||
super(message); | super(message); | ||||
} | } | ||||
public PluginException(Throwable cause) { | |||||
public PluginRuntimeException(Throwable cause) { | |||||
super(cause); | super(cause); | ||||
} | } | ||||
public PluginException(String message, Throwable cause) { | |||||
super(message, cause); | |||||
} | |||||
public PluginException(Throwable cause, String message, Object... args) { | |||||
public PluginRuntimeException(Throwable cause, String message, Object... args) { | |||||
super(StringUtils.format(message, args), cause); | super(StringUtils.format(message, args), cause); | ||||
} | } | ||||
public PluginException(String message, Object... args) { | |||||
public PluginRuntimeException(String message, Object... args) { | |||||
super(StringUtils.format(message, args)); | super(StringUtils.format(message, args)); | ||||
} | } | ||||
* Disables a plugin from being loaded. | * Disables a plugin from being loaded. | ||||
* | * | ||||
* @param pluginId the unique plugin identifier, specified in its metadata | * @param pluginId the unique plugin identifier, specified in its metadata | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
void disablePlugin(String pluginId) throws PluginException; | |||||
void disablePlugin(String pluginId); | |||||
/** | /** | ||||
* Enables a plugin that has previously been disabled. | * Enables a plugin that has previously been disabled. | ||||
* | * | ||||
* @param pluginId the unique plugin identifier, specified in its metadata | * @param pluginId the unique plugin identifier, specified in its metadata | ||||
* @throws PluginRuntimeException if something goes wrong | |||||
*/ | */ | ||||
void enablePlugin(String pluginId) throws PluginException; | |||||
void enablePlugin(String pluginId); | |||||
} | } |
} | } | ||||
@Override | @Override | ||||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||||
public PluginDescriptor find(Path pluginPath) { | |||||
Properties properties = readProperties(pluginPath); | Properties properties = readProperties(pluginPath); | ||||
return createPluginDescriptor(properties); | return createPluginDescriptor(properties); | ||||
} | } | ||||
protected Properties readProperties(Path pluginPath) throws PluginException { | |||||
protected Properties readProperties(Path pluginPath) { | |||||
Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); | Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); | ||||
if (propertiesPath == null) { | if (propertiesPath == null) { | ||||
throw new PluginException("Cannot find the properties path"); | |||||
throw new PluginRuntimeException("Cannot find the properties path"); | |||||
} | } | ||||
log.debug("Lookup plugin descriptor in '{}'", propertiesPath); | log.debug("Lookup plugin descriptor in '{}'", propertiesPath); | ||||
if (Files.notExists(propertiesPath)) { | if (Files.notExists(propertiesPath)) { | ||||
throw new PluginException("Cannot find '{}' path", propertiesPath); | |||||
throw new PluginRuntimeException("Cannot find '{}' path", propertiesPath); | |||||
} | } | ||||
Properties properties = new Properties(); | Properties properties = new Properties(); | ||||
try (InputStream input = Files.newInputStream(propertiesPath)) { | try (InputStream input = Files.newInputStream(propertiesPath)) { | ||||
properties.load(input); | properties.load(input); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
return properties; | return properties; | ||||
} | } | ||||
protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) throws PluginException { | |||||
protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) { | |||||
if (Files.isDirectory(pluginPath)) { | if (Files.isDirectory(pluginPath)) { | ||||
return pluginPath.resolve(Paths.get(propertiesFileName)); | return pluginPath.resolve(Paths.get(propertiesFileName)); | ||||
} else { | } else { | ||||
try { | try { | ||||
return FileUtils.getPath(pluginPath, propertiesFileName); | return FileUtils.getPath(pluginPath, propertiesFileName); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
throw new PluginException(e); | |||||
throw new PluginRuntimeException(e); | |||||
} | } | ||||
} | } | ||||
} | } |
@Override | @Override | ||||
@SuppressWarnings("unchecked") | @SuppressWarnings("unchecked") | ||||
public <T> T create(Class<T> extensionClass) throws PluginException { | |||||
public <T> T create(Class<T> extensionClass) { | |||||
String extensionClassName = extensionClass.getName(); | String extensionClassName = extensionClass.getName(); | ||||
if (cache.containsKey(extensionClassName)) { | if (cache.containsKey(extensionClassName)) { | ||||
return (T) cache.get(extensionClassName); | return (T) cache.get(extensionClassName); |
@Test | @Test | ||||
public void testNotFound() { | public void testNotFound() { | ||||
PluginDescriptorFinder descriptorFinder = new CompoundPluginDescriptorFinder(); | PluginDescriptorFinder descriptorFinder = new CompoundPluginDescriptorFinder(); | ||||
assertThrows(PluginException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||||
assertThrows(PluginRuntimeException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||||
} | } | ||||
@Test | @Test |
* Test of create method, of class DefaultExtensionFactory. | * Test of create method, of class DefaultExtensionFactory. | ||||
*/ | */ | ||||
@Test | @Test | ||||
public void testCreate() throws PluginException { | |||||
public void testCreate() { | |||||
assertNotNull(extensionFactory.create(TestExtension.class)); | assertNotNull(extensionFactory.create(TestExtension.class)); | ||||
} | } | ||||
*/ | */ | ||||
@Test | @Test | ||||
public void testCreateFailConstructor() { | public void testCreateFailConstructor() { | ||||
assertThrows(PluginException.class, () -> extensionFactory.create(FailTestExtension.class)); | |||||
assertThrows(PluginRuntimeException.class, () -> extensionFactory.create(FailTestExtension.class)); | |||||
} | } | ||||
} | } |
} | } | ||||
@Test | @Test | ||||
public void validateOK() throws PluginException { | |||||
public void validateOK() { | |||||
pluginManager.validatePluginDescriptor(pluginDescriptor); | pluginManager.validatePluginDescriptor(pluginDescriptor); | ||||
} | } | ||||
@Test | @Test | ||||
public void validateFailsOnId() { | public void validateFailsOnId() { | ||||
pluginDescriptor.setPluginId(""); | pluginDescriptor.setPluginId(""); | ||||
assertThrows(PluginException.class, () -> pluginManager.validatePluginDescriptor(pluginDescriptor)); | |||||
assertThrows(PluginRuntimeException.class, () -> pluginManager.validatePluginDescriptor(pluginDescriptor)); | |||||
} | } | ||||
@Test | @Test | ||||
public void validateFailsOnVersion() { | public void validateFailsOnVersion() { | ||||
pluginDescriptor.setPluginVersion(null); | pluginDescriptor.setPluginVersion(null); | ||||
assertThrows(PluginException.class, () -> pluginManager.validatePluginDescriptor(pluginDescriptor)); | |||||
assertThrows(PluginRuntimeException.class, () -> pluginManager.validatePluginDescriptor(pluginDescriptor)); | |||||
} | } | ||||
@Test | @Test | ||||
public void validateNoPluginClass() throws PluginException { | |||||
public void validateNoPluginClass() { | |||||
pluginManager.validatePluginDescriptor(pluginDescriptor); | pluginManager.validatePluginDescriptor(pluginDescriptor); | ||||
assertEquals(Plugin.class.getName(), pluginDescriptor.getPluginClass()); | assertEquals(Plugin.class.getName(), pluginDescriptor.getPluginClass()); | ||||
} | } |
* Test of {@link DefaultPluginRepository#deletePluginPath(Path)} method. | * Test of {@link DefaultPluginRepository#deletePluginPath(Path)} method. | ||||
*/ | */ | ||||
@Test | @Test | ||||
public void testDeletePluginPath() throws PluginException { | |||||
public void testDeletePluginPath() { | |||||
PluginRepository repository = new DefaultPluginRepository(pluginsPath); | PluginRepository repository = new DefaultPluginRepository(pluginsPath); | ||||
assertTrue(Files.exists(pluginsPath.resolve("plugin-1.zip"))); | assertTrue(Files.exists(pluginsPath.resolve("plugin-1.zip"))); |
// Verify the second plugin is not loaded as it has the same metadata | // Verify the second plugin is not loaded as it has the same metadata | ||||
pluginManager.loadPluginFromPath(plugin2.path()); | pluginManager.loadPluginFromPath(plugin2.path()); | ||||
fail("Expected loadPluginFromPath to fail"); | fail("Expected loadPluginFromPath to fail"); | ||||
} catch (PluginException e) { | |||||
} catch (PluginRuntimeException e) { | |||||
// Check the path of the loaded plugin remains the same | // Check the path of the loaded plugin remains the same | ||||
PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId); | PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId); | ||||
assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path)); | assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path)); | ||||
// Verify the second plugin is not loaded as it has the same pluginId | // Verify the second plugin is not loaded as it has the same pluginId | ||||
pluginManager.loadPluginFromPath(plugin2.path()); | pluginManager.loadPluginFromPath(plugin2.path()); | ||||
fail("Expected loadPluginFromPath to fail"); | fail("Expected loadPluginFromPath to fail"); | ||||
} catch (PluginException e) { | |||||
} catch (PluginRuntimeException e) { | |||||
// Check the path and version of the loaded plugin remain the same | // Check the path and version of the loaded plugin remain the same | ||||
PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId); | PluginWrapper loadedPlugin = pluginManager.getPlugin(pluginId); | ||||
assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path)); | assertThat(loadedPlugin.getPluginPath(), equalTo(loadedPlugin1Path)); |
@Test | @Test | ||||
public void testFindNotFound() { | public void testFindNotFound() { | ||||
PluginDescriptorFinder descriptorFinder = new ManifestPluginDescriptorFinder(); | PluginDescriptorFinder descriptorFinder = new ManifestPluginDescriptorFinder(); | ||||
assertThrows(PluginException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||||
assertThrows(PluginRuntimeException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||||
} | } | ||||
private Manifest getPlugin1Manifest() { | private Manifest getPlugin1Manifest() { |
@Test | @Test | ||||
public void testNotFound() { | public void testNotFound() { | ||||
PluginDescriptorFinder descriptorFinder = new PropertiesPluginDescriptorFinder(); | PluginDescriptorFinder descriptorFinder = new PropertiesPluginDescriptorFinder(); | ||||
assertThrows(PluginException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||||
assertThrows(PluginRuntimeException.class, () -> descriptorFinder.find(pluginsPath.resolve("test-plugin-3"))); | |||||
} | } | ||||
private Properties getPlugin1Properties() { | private Properties getPlugin1Properties() { |
public class SingletonExtensionFactoryTest { | public class SingletonExtensionFactoryTest { | ||||
@Test | @Test | ||||
public void create() throws PluginException { | |||||
public void create() { | |||||
ExtensionFactory extensionFactory = new SingletonExtensionFactory(); | ExtensionFactory extensionFactory = new SingletonExtensionFactory(); | ||||
Object extensionOne = extensionFactory.create(TestExtension.class); | Object extensionOne = extensionFactory.create(TestExtension.class); | ||||
Object extensionTwo = extensionFactory.create(TestExtension.class); | Object extensionTwo = extensionFactory.create(TestExtension.class); | ||||
} | } | ||||
@Test | @Test | ||||
public void createNewEachTime() throws PluginException { | |||||
public void createNewEachTime() { | |||||
ExtensionFactory extensionFactory = new SingletonExtensionFactory(FailTestExtension.class.getName()); | ExtensionFactory extensionFactory = new SingletonExtensionFactory(FailTestExtension.class.getName()); | ||||
Object extensionOne = extensionFactory.create(TestExtension.class); | Object extensionOne = extensionFactory.create(TestExtension.class); | ||||
Object extensionTwo = extensionFactory.create(TestExtension.class); | Object extensionTwo = extensionFactory.create(TestExtension.class); |