mirror of https://github.com/pf4j/pf4j.git
Decebal Suiu
8 years ago
30 changed files with 1502 additions and 1247 deletions
@ -0,0 +1,800 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2016 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package ro.fortsoft.pf4j; |
||||||
|
|
||||||
|
import com.github.zafarkhaja.semver.Version; |
||||||
|
import com.github.zafarkhaja.semver.expr.Expression; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.io.Closeable; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.nio.file.Paths; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class implements the boilerplate plugin code that any {@link PluginManager} |
||||||
|
* implementation would have to support. |
||||||
|
* It helps cut the noise out of the subclass that handles plugin management. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public abstract class AbstractPluginManager implements PluginManager { |
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AbstractPluginManager.class); |
||||||
|
|
||||||
|
private Path pluginsRoot; |
||||||
|
|
||||||
|
private ExtensionFinder extensionFinder; |
||||||
|
|
||||||
|
private PluginDescriptorFinder pluginDescriptorFinder; |
||||||
|
|
||||||
|
/* |
||||||
|
* A map of plugins this manager is responsible for (the key is the 'pluginId'). |
||||||
|
*/ |
||||||
|
protected Map<String, PluginWrapper> plugins; |
||||||
|
|
||||||
|
/* |
||||||
|
* A map of plugin class loaders (he key is the 'pluginId'). |
||||||
|
*/ |
||||||
|
private Map<String, ClassLoader> pluginClassLoaders; |
||||||
|
|
||||||
|
/* |
||||||
|
* A list with unresolved plugins (unresolved dependency). |
||||||
|
*/ |
||||||
|
private List<PluginWrapper> unresolvedPlugins; |
||||||
|
|
||||||
|
/** |
||||||
|
* A list with resolved plugins (resolved dependency). |
||||||
|
*/ |
||||||
|
private List<PluginWrapper> resolvedPlugins; |
||||||
|
|
||||||
|
/* |
||||||
|
* A list with started plugins. |
||||||
|
*/ |
||||||
|
private List<PluginWrapper> startedPlugins; |
||||||
|
|
||||||
|
/* |
||||||
|
* The registered {@link PluginStateListener}s. |
||||||
|
*/ |
||||||
|
private List<PluginStateListener> pluginStateListeners; |
||||||
|
|
||||||
|
/* |
||||||
|
* Cache value for the runtime mode. |
||||||
|
* No need to re-read it because it wont change at runtime. |
||||||
|
*/ |
||||||
|
private RuntimeMode runtimeMode; |
||||||
|
|
||||||
|
/* |
||||||
|
* The system version used for comparisons to the plugin requires attribute. |
||||||
|
*/ |
||||||
|
private Version systemVersion = Version.forIntegers(0, 0, 0); |
||||||
|
|
||||||
|
/** |
||||||
|
* A relation between 'pluginPath' and 'pluginId' |
||||||
|
*/ |
||||||
|
private Map<Path, String> pathToIdMap; |
||||||
|
|
||||||
|
private PluginRepository pluginRepository; |
||||||
|
private PluginFactory pluginFactory; |
||||||
|
private ExtensionFactory extensionFactory; |
||||||
|
private PluginStatusProvider pluginStatusProvider; |
||||||
|
private DependencyResolver dependencyResolver; |
||||||
|
private PluginLoader pluginLoader; |
||||||
|
|
||||||
|
/** |
||||||
|
* The plugins root is supplied by {@code System.getProperty("pf4j.pluginsDir", "plugins")}. |
||||||
|
*/ |
||||||
|
public AbstractPluginManager() { |
||||||
|
initialize(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs {@code DefaultPluginManager} which the given plugins root. |
||||||
|
* |
||||||
|
* @param pluginsRoot the root to search for plugins |
||||||
|
*/ |
||||||
|
public AbstractPluginManager(Path pluginsRoot) { |
||||||
|
this.pluginsRoot = pluginsRoot; |
||||||
|
|
||||||
|
initialize(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSystemVersion(Version version) { |
||||||
|
systemVersion = version; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Version getSystemVersion() { |
||||||
|
return systemVersion; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a copy of plugins. |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<PluginWrapper> getPlugins() { |
||||||
|
return new ArrayList<>(plugins.values()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a copy of plugins with that state. |
||||||
|
* |
||||||
|
* @param pluginState |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public List<PluginWrapper> getPlugins(PluginState pluginState) { |
||||||
|
List<PluginWrapper> plugins = new ArrayList<>(); |
||||||
|
for (PluginWrapper plugin : getPlugins()) { |
||||||
|
if (pluginState.equals(plugin.getPluginState())) { |
||||||
|
plugins.add(plugin); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return plugins; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<PluginWrapper> getResolvedPlugins() { |
||||||
|
return resolvedPlugins; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<PluginWrapper> getUnresolvedPlugins() { |
||||||
|
return unresolvedPlugins; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<PluginWrapper> getStartedPlugins() { |
||||||
|
return startedPlugins; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public PluginWrapper getPlugin(String pluginId) { |
||||||
|
return plugins.get(pluginId); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String loadPlugin(Path pluginPath) { |
||||||
|
if ((pluginPath == null) || Files.notExists(pluginPath)) { |
||||||
|
throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginPath)); |
||||||
|
} |
||||||
|
|
||||||
|
log.debug("Loading plugin from '{}'", pluginPath); |
||||||
|
|
||||||
|
try { |
||||||
|
PluginWrapper pluginWrapper = loadPluginFromPath(pluginPath); |
||||||
|
// TODO uninstalled plugin dependencies?
|
||||||
|
getUnresolvedPlugins().remove(pluginWrapper); |
||||||
|
getResolvedPlugins().add(pluginWrapper); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, null)); |
||||||
|
|
||||||
|
return pluginWrapper.getDescriptor().getPluginId(); |
||||||
|
} catch (PluginException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Load plugins. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void loadPlugins() { |
||||||
|
log.debug("Lookup plugins in '{}'", pluginsRoot); |
||||||
|
// check for plugins root
|
||||||
|
if (Files.notExists(pluginsRoot) || !Files.isDirectory(pluginsRoot)) { |
||||||
|
log.error("No '{}' root", pluginsRoot); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// get all plugin paths from repository
|
||||||
|
List<Path> pluginPaths = pluginRepository.getPluginPaths(); |
||||||
|
|
||||||
|
// check for no plugins
|
||||||
|
if (pluginPaths.isEmpty()) { |
||||||
|
log.info("No plugins"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
log.debug("Found {} possible plugins: {}", pluginPaths.size(), pluginPaths); |
||||||
|
|
||||||
|
// load plugins from plugin paths
|
||||||
|
// TODO
|
||||||
|
for (Path pluginPath : pluginPaths) { |
||||||
|
try { |
||||||
|
loadPluginFromPath(pluginPath); |
||||||
|
} catch (PluginException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// resolve 'unresolvedPlugins'
|
||||||
|
try { |
||||||
|
resolvePlugins(); |
||||||
|
} catch (PluginException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean unloadPlugin(String pluginId) { |
||||||
|
try { |
||||||
|
PluginState pluginState = stopPlugin(pluginId); |
||||||
|
if (PluginState.STARTED == pluginState) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId); |
||||||
|
PluginDescriptor descriptor = pluginWrapper.getDescriptor(); |
||||||
|
List<PluginDependency> dependencies = descriptor.getDependencies(); |
||||||
|
for (PluginDependency dependency : dependencies) { |
||||||
|
if (!unloadPlugin(dependency.getPluginId())) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// remove the plugin
|
||||||
|
plugins.remove(pluginId); |
||||||
|
getResolvedPlugins().remove(pluginWrapper); |
||||||
|
pathToIdMap.remove(pluginWrapper.getPluginPath()); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); |
||||||
|
|
||||||
|
// remove the classloader
|
||||||
|
Map<String, ClassLoader> pluginClassLoaders = getPluginClassLoaders(); |
||||||
|
if (pluginClassLoaders.containsKey(pluginId)) { |
||||||
|
ClassLoader classLoader = pluginClassLoaders.remove(pluginId); |
||||||
|
if (classLoader instanceof Closeable) { |
||||||
|
try { |
||||||
|
((Closeable) classLoader).close(); |
||||||
|
} catch (IOException e) { |
||||||
|
log.error("Cannot close classloader", e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} catch (IllegalArgumentException e) { |
||||||
|
// ignore not found exceptions because this method is recursive
|
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean deletePlugin(String pluginId) { |
||||||
|
if (!plugins.containsKey(pluginId)) { |
||||||
|
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); |
||||||
|
} |
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId); |
||||||
|
PluginState pluginState = stopPlugin(pluginId); |
||||||
|
if (PluginState.STARTED == pluginState) { |
||||||
|
log.error("Failed to stop plugin '{}' on delete", pluginId); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!unloadPlugin(pluginId)) { |
||||||
|
log.error("Failed to unload plugin '{}' on delete", pluginId); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
Path pluginPath = pluginWrapper.getPluginPath(); |
||||||
|
|
||||||
|
pluginRepository.deletePluginPath(pluginPath); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Start all active plugins. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void startPlugins() { |
||||||
|
for (PluginWrapper pluginWrapper : resolvedPlugins) { |
||||||
|
PluginState pluginState = pluginWrapper.getPluginState(); |
||||||
|
if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { |
||||||
|
try { |
||||||
|
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); |
||||||
|
log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
pluginWrapper.getPlugin().start(); |
||||||
|
pluginWrapper.setPluginState(PluginState.STARTED); |
||||||
|
startedPlugins.add(pluginWrapper); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); |
||||||
|
} catch (PluginException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Start the specified plugin and it's dependencies. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public PluginState startPlugin(String pluginId) { |
||||||
|
if (!plugins.containsKey(pluginId)) { |
||||||
|
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); |
||||||
|
} |
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId); |
||||||
|
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); |
||||||
|
PluginState pluginState = pluginWrapper.getPluginState(); |
||||||
|
if (PluginState.STARTED == pluginState) { |
||||||
|
log.debug("Already started plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
return PluginState.STARTED; |
||||||
|
} |
||||||
|
|
||||||
|
if (PluginState.DISABLED == pluginState) { |
||||||
|
// automatically enable plugin on manual plugin start
|
||||||
|
if (!enablePlugin(pluginId)) { |
||||||
|
return pluginState; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (PluginDependency dependency : pluginDescriptor.getDependencies()) { |
||||||
|
startPlugin(dependency.getPluginId()); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
log.info("Start plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
pluginWrapper.getPlugin().start(); |
||||||
|
pluginWrapper.setPluginState(PluginState.STARTED); |
||||||
|
startedPlugins.add(pluginWrapper); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); |
||||||
|
} catch (PluginException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
return pluginWrapper.getPluginState(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Stop all active plugins. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void stopPlugins() { |
||||||
|
// stop started plugins in reverse order
|
||||||
|
Collections.reverse(startedPlugins); |
||||||
|
Iterator<PluginWrapper> itr = startedPlugins.iterator(); |
||||||
|
while (itr.hasNext()) { |
||||||
|
PluginWrapper pluginWrapper = itr.next(); |
||||||
|
PluginState pluginState = pluginWrapper.getPluginState(); |
||||||
|
if (PluginState.STARTED == pluginState) { |
||||||
|
try { |
||||||
|
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); |
||||||
|
log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
pluginWrapper.getPlugin().stop(); |
||||||
|
pluginWrapper.setPluginState(PluginState.STOPPED); |
||||||
|
itr.remove(); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); |
||||||
|
} catch (PluginException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Stop the specified plugin and it's dependencies. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public PluginState stopPlugin(String pluginId) { |
||||||
|
return stopPlugin(pluginId, true); |
||||||
|
} |
||||||
|
|
||||||
|
private PluginState stopPlugin(String pluginId, boolean stopDependents) { |
||||||
|
if (!plugins.containsKey(pluginId)) { |
||||||
|
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); |
||||||
|
} |
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId); |
||||||
|
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); |
||||||
|
PluginState pluginState = pluginWrapper.getPluginState(); |
||||||
|
if (PluginState.STOPPED == pluginState) { |
||||||
|
log.debug("Already stopped plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
return PluginState.STOPPED; |
||||||
|
} |
||||||
|
|
||||||
|
// test for disabled plugin
|
||||||
|
if (PluginState.DISABLED == pluginState) { |
||||||
|
// do nothing
|
||||||
|
return pluginState; |
||||||
|
} |
||||||
|
|
||||||
|
if (stopDependents) { |
||||||
|
List<String> dependents = dependencyResolver.getDependents(pluginId); |
||||||
|
while (!dependents.isEmpty()) { |
||||||
|
String dependent = dependents.remove(0); |
||||||
|
stopPlugin(dependent, false); |
||||||
|
dependents.addAll(0, dependencyResolver.getDependents(dependent)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
log.info("Stop plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
pluginWrapper.getPlugin().stop(); |
||||||
|
pluginWrapper.setPluginState(PluginState.STOPPED); |
||||||
|
startedPlugins.remove(pluginWrapper); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); |
||||||
|
} catch (PluginException e) { |
||||||
|
log.error(e.getMessage(), e); |
||||||
|
} |
||||||
|
|
||||||
|
return pluginWrapper.getPluginState(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean disablePlugin(String pluginId) { |
||||||
|
if (!plugins.containsKey(pluginId)) { |
||||||
|
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); |
||||||
|
} |
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId); |
||||||
|
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); |
||||||
|
PluginState pluginState = pluginWrapper.getPluginState(); |
||||||
|
if (PluginState.DISABLED == pluginState) { |
||||||
|
log.debug("Already disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (PluginState.STOPPED == stopPlugin(pluginId)) { |
||||||
|
pluginWrapper.setPluginState(PluginState.DISABLED); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); |
||||||
|
|
||||||
|
if (!pluginStatusProvider.disablePlugin(pluginId)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
log.info("Disabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean enablePlugin(String pluginId) { |
||||||
|
if (!plugins.containsKey(pluginId)) { |
||||||
|
throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); |
||||||
|
} |
||||||
|
|
||||||
|
PluginWrapper pluginWrapper = getPlugin(pluginId); |
||||||
|
if (!isPluginValid(pluginWrapper)) { |
||||||
|
log.warn("Plugin '{}:{}' can not be enabled", pluginWrapper.getPluginId(), |
||||||
|
pluginWrapper.getDescriptor().getVersion()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); |
||||||
|
PluginState pluginState = pluginWrapper.getPluginState(); |
||||||
|
if (PluginState.DISABLED != pluginState) { |
||||||
|
log.debug("Plugin '{}:{}' is not disabled", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (!pluginStatusProvider.enablePlugin(pluginId)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
pluginWrapper.setPluginState(PluginState.CREATED); |
||||||
|
|
||||||
|
firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); |
||||||
|
|
||||||
|
log.info("Enabled plugin '{}:{}'", pluginDescriptor.getPluginId(), pluginDescriptor.getVersion()); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get plugin class loader for this path. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public ClassLoader getPluginClassLoader(String pluginId) { |
||||||
|
return pluginClassLoaders.get(pluginId); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public <T> List<T> getExtensions(Class<T> type) { |
||||||
|
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type); |
||||||
|
List<T> extensions = new ArrayList<>(extensionsWrapper.size()); |
||||||
|
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { |
||||||
|
extensions.add(extensionWrapper.getExtension()); |
||||||
|
} |
||||||
|
|
||||||
|
return extensions; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public <T> List<T> getExtensions(Class<T> type, String pluginId) { |
||||||
|
List<ExtensionWrapper<T>> extensionsWrapper = extensionFinder.find(type, pluginId); |
||||||
|
List<T> extensions = new ArrayList<>(extensionsWrapper.size()); |
||||||
|
for (ExtensionWrapper<T> extensionWrapper : extensionsWrapper) { |
||||||
|
extensions.add(extensionWrapper.getExtension()); |
||||||
|
} |
||||||
|
|
||||||
|
return extensions; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@SuppressWarnings("unchecked") |
||||||
|
public List getExtensions(String pluginId) { |
||||||
|
List<ExtensionWrapper> extensionsWrapper = extensionFinder.find(pluginId); |
||||||
|
List extensions = new ArrayList<>(extensionsWrapper.size()); |
||||||
|
for (ExtensionWrapper extensionWrapper : extensionsWrapper) { |
||||||
|
extensions.add(extensionWrapper.getExtension()); |
||||||
|
} |
||||||
|
|
||||||
|
return extensions; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Set<String> getExtensionClassNames(String pluginId) { |
||||||
|
return extensionFinder.findClassNames(pluginId); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ExtensionFactory getExtensionFactory() { |
||||||
|
return extensionFactory; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO remove
|
||||||
|
public PluginLoader getPluginLoader() { |
||||||
|
return pluginLoader; |
||||||
|
} |
||||||
|
|
||||||
|
public Path getPluginsRoot() { |
||||||
|
return pluginsRoot; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public RuntimeMode getRuntimeMode() { |
||||||
|
if (runtimeMode == null) { |
||||||
|
// retrieves the runtime mode from system
|
||||||
|
String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString()); |
||||||
|
runtimeMode = RuntimeMode.byName(modeAsString); |
||||||
|
} |
||||||
|
|
||||||
|
return runtimeMode; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public PluginWrapper whichPlugin(Class<?> clazz) { |
||||||
|
ClassLoader classLoader = clazz.getClassLoader(); |
||||||
|
for (PluginWrapper plugin : resolvedPlugins) { |
||||||
|
if (plugin.getPluginClassLoader() == classLoader) { |
||||||
|
return plugin; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public synchronized void addPluginStateListener(PluginStateListener listener) { |
||||||
|
pluginStateListeners.add(listener); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public synchronized void removePluginStateListener(PluginStateListener listener) { |
||||||
|
pluginStateListeners.remove(listener); |
||||||
|
} |
||||||
|
|
||||||
|
public Version getVersion() { |
||||||
|
String version = null; |
||||||
|
|
||||||
|
Package pf4jPackage = getClass().getPackage(); |
||||||
|
if (pf4jPackage != null) { |
||||||
|
version = pf4jPackage.getImplementationVersion(); |
||||||
|
if (version == null) { |
||||||
|
version = pf4jPackage.getSpecificationVersion(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return (version != null) ? Version.valueOf(version) : Version.forIntegers(0, 0, 0); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract PluginRepository createPluginRepository(); |
||||||
|
|
||||||
|
protected abstract PluginFactory createPluginFactory(); |
||||||
|
|
||||||
|
protected abstract ExtensionFactory createExtensionFactory(); |
||||||
|
|
||||||
|
protected abstract PluginDescriptorFinder createPluginDescriptorFinder(); |
||||||
|
|
||||||
|
protected abstract ExtensionFinder createExtensionFinder(); |
||||||
|
|
||||||
|
protected abstract PluginStatusProvider createPluginStatusProvider(); |
||||||
|
|
||||||
|
protected abstract PluginLoader createPluginLoader(); |
||||||
|
|
||||||
|
protected PluginDescriptorFinder getPluginDescriptorFinder() { |
||||||
|
return pluginDescriptorFinder; |
||||||
|
} |
||||||
|
|
||||||
|
protected PluginFactory getPluginFactory() { |
||||||
|
return pluginFactory; |
||||||
|
} |
||||||
|
|
||||||
|
protected Map<String, ClassLoader> getPluginClassLoaders() { |
||||||
|
return pluginClassLoaders; |
||||||
|
} |
||||||
|
|
||||||
|
protected void initialize() { |
||||||
|
plugins = new HashMap<>(); |
||||||
|
pluginClassLoaders = new HashMap<>(); |
||||||
|
unresolvedPlugins = new ArrayList<>(); |
||||||
|
resolvedPlugins = new ArrayList<>(); |
||||||
|
startedPlugins = new ArrayList<>(); |
||||||
|
|
||||||
|
pluginStateListeners = new ArrayList<>(); |
||||||
|
pathToIdMap = new HashMap<>(); |
||||||
|
|
||||||
|
if (pluginsRoot == null) { |
||||||
|
pluginsRoot = createPluginsRoot(); |
||||||
|
} |
||||||
|
|
||||||
|
System.setProperty("pf4j.pluginsDir", pluginsRoot.toString()); |
||||||
|
|
||||||
|
dependencyResolver = new DependencyResolver(); |
||||||
|
|
||||||
|
pluginRepository = createPluginRepository(); |
||||||
|
pluginFactory = createPluginFactory(); |
||||||
|
extensionFactory = createExtensionFactory(); |
||||||
|
pluginDescriptorFinder = createPluginDescriptorFinder(); |
||||||
|
extensionFinder = createExtensionFinder(); |
||||||
|
pluginStatusProvider = createPluginStatusProvider(); |
||||||
|
pluginLoader = createPluginLoader(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add the possibility to override the plugins root. |
||||||
|
* If a "pf4j.pluginsDir" system property is defined than this method returns |
||||||
|
* that root. |
||||||
|
* If getRuntimeMode() returns RuntimeMode.DEVELOPMENT than a |
||||||
|
* DEVELOPMENT_PLUGINS_DIRECTORY ("../plugins") is returned else this method returns |
||||||
|
* DEFAULT_PLUGINS_DIRECTORY ("plugins"). |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
protected Path createPluginsRoot() { |
||||||
|
String pluginsDir = System.getProperty("pf4j.pluginsDir"); |
||||||
|
if (pluginsDir == null) { |
||||||
|
if (isDevelopment()) { |
||||||
|
pluginsDir = "../plugins"; |
||||||
|
} else { |
||||||
|
pluginsDir = "plugins"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return Paths.get(pluginsDir); |
||||||
|
} |
||||||
|
|
||||||
|
protected boolean isPluginValid(PluginWrapper pluginWrapper) { |
||||||
|
Expression requires = pluginWrapper.getDescriptor().getRequires(); |
||||||
|
Version system = getSystemVersion(); |
||||||
|
if (requires.interpret(system)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
log.warn("Plugin '{}:{}' requires a minimum system version of {}", |
||||||
|
pluginWrapper.getPluginId(), |
||||||
|
pluginWrapper.getDescriptor().getVersion(), |
||||||
|
requires); |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
protected boolean isPluginDisabled(String pluginId) { |
||||||
|
return pluginStatusProvider.isPluginDisabled(pluginId); |
||||||
|
} |
||||||
|
|
||||||
|
protected void resolvePlugins() throws PluginException { |
||||||
|
resolveDependencies(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void resolveDependencies() throws PluginException { |
||||||
|
dependencyResolver.resolve(unresolvedPlugins); |
||||||
|
resolvedPlugins = dependencyResolver.getSortedPlugins(); |
||||||
|
for (PluginWrapper pluginWrapper : resolvedPlugins) { |
||||||
|
unresolvedPlugins.remove(pluginWrapper); |
||||||
|
log.info("Plugin '{}' resolved", pluginWrapper.getDescriptor().getPluginId()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected synchronized void firePluginStateEvent(PluginStateEvent event) { |
||||||
|
for (PluginStateListener listener : pluginStateListeners) { |
||||||
|
log.debug("Fire '{}' to '{}'", event, listener); |
||||||
|
listener.pluginStateChanged(event); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { |
||||||
|
// test for plugin duplication
|
||||||
|
if (plugins.get(pathToIdMap.get(pluginPath)) != null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// retrieves the plugin descriptor
|
||||||
|
log.debug("Find plugin descriptor '{}'", pluginPath); |
||||||
|
PluginDescriptor pluginDescriptor = getPluginDescriptorFinder().find(pluginPath); |
||||||
|
log.debug("Descriptor {}", pluginDescriptor); |
||||||
|
String pluginClassName = pluginDescriptor.getPluginClass(); |
||||||
|
log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); |
||||||
|
|
||||||
|
// load plugin
|
||||||
|
log.debug("Loading plugin '{}'", pluginPath); |
||||||
|
ClassLoader pluginClassLoader = getPluginLoader().loadPlugin(pluginPath, pluginDescriptor); |
||||||
|
log.debug("Loaded plugin '{}' with class loader '{}'", pluginPath, pluginClassLoader); |
||||||
|
|
||||||
|
// create the plugin wrapper
|
||||||
|
log.debug("Creating wrapper for plugin '{}'", pluginPath); |
||||||
|
PluginWrapper pluginWrapper = new PluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader); |
||||||
|
pluginWrapper.setPluginFactory(getPluginFactory()); |
||||||
|
pluginWrapper.setRuntimeMode(getRuntimeMode()); |
||||||
|
|
||||||
|
// test for disabled plugin
|
||||||
|
if (isPluginDisabled(pluginDescriptor.getPluginId())) { |
||||||
|
log.info("Plugin '{}' is disabled", pluginPath); |
||||||
|
pluginWrapper.setPluginState(PluginState.DISABLED); |
||||||
|
} |
||||||
|
|
||||||
|
// validate the plugin
|
||||||
|
if (!isPluginValid(pluginWrapper)) { |
||||||
|
log.info("Plugin '{}' is disabled", pluginPath); |
||||||
|
pluginWrapper.setPluginState(PluginState.DISABLED); |
||||||
|
} |
||||||
|
|
||||||
|
log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); |
||||||
|
|
||||||
|
String pluginId = pluginDescriptor.getPluginId(); |
||||||
|
|
||||||
|
// add plugin to the list with plugins
|
||||||
|
plugins.put(pluginId, pluginWrapper); |
||||||
|
getUnresolvedPlugins().add(pluginWrapper); |
||||||
|
|
||||||
|
// add plugin class loader to the list with class loaders
|
||||||
|
getPluginClassLoaders().put(pluginId, pluginClassLoader); |
||||||
|
|
||||||
|
return pluginWrapper; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO add this method in PluginManager as default method for Java 8.
|
||||||
|
protected boolean isDevelopment() { |
||||||
|
return RuntimeMode.DEVELOPMENT.equals(getRuntimeMode()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2012 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package ro.fortsoft.pf4j; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileFilter; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Decebal Suiu |
||||||
|
* @author Mário Franco |
||||||
|
*/ |
||||||
|
public class BasePluginRepository implements PluginRepository { |
||||||
|
|
||||||
|
protected final Path pluginsRoot; |
||||||
|
protected FileFilter filter; |
||||||
|
|
||||||
|
public BasePluginRepository(Path pluginsRoot) { |
||||||
|
this.pluginsRoot = pluginsRoot; |
||||||
|
} |
||||||
|
|
||||||
|
public BasePluginRepository(Path pluginsRoot, FileFilter filter) { |
||||||
|
this.pluginsRoot = pluginsRoot; |
||||||
|
this.filter = filter; |
||||||
|
} |
||||||
|
|
||||||
|
public void setFilter(FileFilter filter) { |
||||||
|
this.filter = filter; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<Path> getPluginPaths() { |
||||||
|
File[] files = pluginsRoot.toFile().listFiles(filter); |
||||||
|
|
||||||
|
if ((files == null) || files.length == 0) { |
||||||
|
return Collections.emptyList(); |
||||||
|
} |
||||||
|
|
||||||
|
List<Path> paths = new ArrayList<>(files.length); |
||||||
|
for (File file : files) { |
||||||
|
paths.add(file.toPath()); |
||||||
|
} |
||||||
|
|
||||||
|
return paths; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean deletePluginPath(Path pluginPath) { |
||||||
|
try { |
||||||
|
return Files.deleteIfExists(pluginPath); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2016 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package ro.fortsoft.pf4j; |
||||||
|
|
||||||
|
import ro.fortsoft.pf4j.util.FileUtils; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Load all information needed by a plugin. |
||||||
|
* This means add to classpath all jar files from {@code lib} directory |
||||||
|
* and all class files from {@code classes}. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class DefaultPluginLoader implements PluginLoader { |
||||||
|
|
||||||
|
protected PluginManager pluginManager; |
||||||
|
protected PluginClasspath pluginClasspath; |
||||||
|
|
||||||
|
public DefaultPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { |
||||||
|
this.pluginManager = pluginManager; |
||||||
|
this.pluginClasspath = pluginClasspath; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { |
||||||
|
PluginClassLoader pluginClassLoader = createPluginClassLoader(pluginPath, pluginDescriptor); |
||||||
|
|
||||||
|
loadClasses(pluginPath, pluginClassLoader); |
||||||
|
loadJars(pluginPath, pluginClassLoader); |
||||||
|
|
||||||
|
return pluginClassLoader; |
||||||
|
} |
||||||
|
|
||||||
|
protected PluginClassLoader createPluginClassLoader(Path pluginPath, PluginDescriptor pluginDescriptor) { |
||||||
|
return new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add all {@code *.class} files from {@code classes} directories to plugin class loader. |
||||||
|
*/ |
||||||
|
protected void loadClasses(Path pluginPath, PluginClassLoader pluginClassLoader) { |
||||||
|
for (String directory : pluginClasspath.getClassesDirectories()) { |
||||||
|
File file = pluginPath.resolve(directory).toFile(); |
||||||
|
if (file.exists() && file.isDirectory()) { |
||||||
|
pluginClassLoader.addFile(file); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Add all {@code *.jar} files from {@code lib} directories to plugin class loader. |
||||||
|
*/ |
||||||
|
protected void loadJars(Path pluginPath, PluginClassLoader pluginClassLoader) { |
||||||
|
for (String libDirectory : pluginClasspath.getLibDirectories()) { |
||||||
|
File file = pluginPath.resolve(libDirectory).toFile(); |
||||||
|
List<File> jars = FileUtils.getJars(file); |
||||||
|
for (File jar : jars) { |
||||||
|
pluginClassLoader.addFile(jar); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2016 Decebal Suiu |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package ro.fortsoft.pf4j; |
||||||
|
|
||||||
|
import ro.fortsoft.pf4j.util.AndFileFilter; |
||||||
|
import ro.fortsoft.pf4j.util.DirectoryFileFilter; |
||||||
|
import ro.fortsoft.pf4j.util.HiddenFilter; |
||||||
|
import ro.fortsoft.pf4j.util.JarFileFilter; |
||||||
|
import ro.fortsoft.pf4j.util.NameFileFilter; |
||||||
|
import ro.fortsoft.pf4j.util.NotFileFilter; |
||||||
|
import ro.fortsoft.pf4j.util.OrFileFilter; |
||||||
|
|
||||||
|
import java.io.FileFilter; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.jar.JarFile; |
||||||
|
import java.util.jar.Manifest; |
||||||
|
|
||||||
|
/** |
||||||
|
* It's a {@link PluginManager} that load plugin from a jar file. |
||||||
|
* Actually, a plugin is a fat jar, a jar which contains classes from all the libraries, |
||||||
|
* on which your project depends and, of course, the classes of current project. |
||||||
|
* |
||||||
|
* @author Decebal Suiu |
||||||
|
*/ |
||||||
|
public class JarPluginManager extends DefaultPluginManager { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginRepository createPluginRepository() { |
||||||
|
return new JarPluginRepository(getPluginsRoot(), isDevelopment()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginDescriptorFinder createPluginDescriptorFinder() { |
||||||
|
return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new JarPluginDescriptorFinder(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected PluginLoader createPluginLoader() { |
||||||
|
return new JarPluginLoader(this, pluginClasspath); |
||||||
|
} |
||||||
|
|
||||||
|
class JarPluginRepository extends BasePluginRepository { |
||||||
|
|
||||||
|
public JarPluginRepository(Path pluginsRoot, boolean development) { |
||||||
|
super(pluginsRoot); |
||||||
|
|
||||||
|
if (development) { |
||||||
|
AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); |
||||||
|
pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); |
||||||
|
setFilter(pluginsFilter); |
||||||
|
} else { |
||||||
|
setFilter(new JarFileFilter()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected FileFilter createHiddenPluginFilter(boolean development) { |
||||||
|
OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); |
||||||
|
|
||||||
|
if (development) { |
||||||
|
hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); |
||||||
|
} |
||||||
|
|
||||||
|
return hiddenPluginFilter; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class JarPluginDescriptorFinder extends ManifestPluginDescriptorFinder { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Manifest readManifest(Path pluginPath) throws PluginException { |
||||||
|
try { |
||||||
|
return new JarFile(pluginPath.toFile()).getManifest(); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new PluginException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
class JarPluginLoader extends DefaultPluginLoader { |
||||||
|
|
||||||
|
public JarPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { |
||||||
|
super(pluginManager, pluginClasspath); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { |
||||||
|
if (isDevelopment()) { |
||||||
|
return super.loadPlugin(pluginPath, pluginDescriptor); |
||||||
|
} |
||||||
|
|
||||||
|
PluginClassLoader pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); |
||||||
|
pluginClassLoader.addFile(pluginPath.toFile()); |
||||||
|
|
||||||
|
return pluginClassLoader; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
log4j.rootLogger=DEBUG, Console |
||||||
|
|
||||||
|
# |
||||||
|
# PF4J log |
||||||
|
# |
||||||
|
log4j.logger.ro.fortsoft.pf4j=DEBUG, Console |
||||||
|
# !!! Put the bellow classes on level TRACE when you are in trouble |
||||||
|
log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=WARN, Console |
||||||
|
log4j.logger.ro.fortsoft.pf4j.AbstractExtensionFinder=DEBUG, Console |
||||||
|
log4j.additivity.ro.fortsoft.pf4j=false |
||||||
|
log4j.additivity.ro.fortsoft.pf4j.PluginClassLoader=false |
||||||
|
log4j.additivity.ro.fortsoft.pf4j.AbstractExtensionFinder=false |
||||||
|
|
||||||
|
# |
||||||
|
# Appenders |
||||||
|
# |
||||||
|
log4j.appender.Console=org.apache.log4j.ConsoleAppender |
||||||
|
log4j.appender.Console.layout=org.apache.log4j.PatternLayout |
||||||
|
#log4j.appender.Console.layout.conversionPattern=%-5p - %-32.32c{1} - %m\n |
||||||
|
log4j.appender.Console.layout.ConversionPattern=%d %p %c - %m%n |
Loading…
Reference in new issue