System.out.println("WelcomePlugin.start()"); | System.out.println("WelcomePlugin.start()"); | ||||
// for testing the development mode | // for testing the development mode | ||||
if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { | if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { | ||||
System.out.println(StringUtils.upperCase("WelcomePlugin")); | |||||
System.out.println(StringUtils.upperCase("WelcomePlugin")); | |||||
} | } | ||||
} | } | ||||
@Extension | @Extension | ||||
public static class WelcomeGreeting implements Greeting { | public static class WelcomeGreeting implements Greeting { | ||||
@Override | |||||
@Override | |||||
public String getGreeting() { | public String getGreeting() { | ||||
return "Welcome"; | return "Welcome"; | ||||
} | } |
@Extension(ordinal=1) | @Extension(ordinal=1) | ||||
public static class HelloGreeting implements Greeting { | public static class HelloGreeting implements Greeting { | ||||
@Override | |||||
@Override | |||||
public String getGreeting() { | public String getGreeting() { | ||||
return "Hello"; | return "Hello"; | ||||
} | } |
/* | /* | ||||
Runtime.getRuntime().addShutdownHook(new Thread() { | Runtime.getRuntime().addShutdownHook(new Thread() { | ||||
@Override | |||||
public void run() { | |||||
pluginManager.stopPlugins(); | |||||
} | |||||
@Override | |||||
public void run() { | |||||
pluginManager.stopPlugins(); | |||||
} | |||||
}); | }); | ||||
*/ | */ | ||||
} | } | ||||
private static void printLogo() { | |||||
logger.info(StringUtils.repeat("#", 40)); | |||||
logger.info(StringUtils.center("PF4J-DEMO", 40)); | |||||
logger.info(StringUtils.repeat("#", 40)); | |||||
} | |||||
private static void printLogo() { | |||||
logger.info(StringUtils.repeat("#", 40)); | |||||
logger.info(StringUtils.center("PF4J-DEMO", 40)); | |||||
logger.info(StringUtils.repeat("#", 40)); | |||||
} | |||||
} | } |
* @author Decebal Suiu | * @author Decebal Suiu | ||||
*/ | */ | ||||
public class WelcomePlugin extends Plugin { | public class WelcomePlugin extends Plugin { | ||||
private static final Logger logger = LoggerFactory.getLogger(WelcomePlugin.class); | private static final Logger logger = LoggerFactory.getLogger(WelcomePlugin.class); | ||||
public WelcomePlugin(PluginWrapper wrapper) { | public WelcomePlugin(PluginWrapper wrapper) { | ||||
@Override | @Override | ||||
public void start() { | public void start() { | ||||
logger.info("WelcomePlugin.start()"); | logger.info("WelcomePlugin.start()"); | ||||
logger.info(StringUtils.upperCase("WelcomePlugin")); | |||||
logger.info(StringUtils.upperCase("WelcomePlugin")); | |||||
} | } | ||||
@Override | @Override | ||||
@Extension | @Extension | ||||
public static class WelcomeGreeting implements Greeting { | public static class WelcomeGreeting implements Greeting { | ||||
@Override | |||||
@Override | |||||
public String getGreeting() { | public String getGreeting() { | ||||
return "Welcome"; | return "Welcome"; | ||||
} | } |
@Extension(ordinal=1) | @Extension(ordinal=1) | ||||
public static class HelloGreeting implements Greeting { | public static class HelloGreeting implements Greeting { | ||||
@Override | |||||
@Override | |||||
public String getGreeting() { | public String getGreeting() { | ||||
return "Hello"; | return "Hello"; | ||||
} | } |
protected PluginManager pluginManager; | protected PluginManager pluginManager; | ||||
protected List<ExtensionFinder> finders = new ArrayList<>(); | protected List<ExtensionFinder> finders = new ArrayList<>(); | ||||
public DefaultExtensionFinder(PluginManager pluginManager) { | |||||
public DefaultExtensionFinder(PluginManager pluginManager) { | |||||
this.pluginManager = pluginManager; | this.pluginManager = pluginManager; | ||||
add(new LegacyExtensionFinder(pluginManager)); | add(new LegacyExtensionFinder(pluginManager)); |
*/ | */ | ||||
public class DefaultPluginManager extends AbstractPluginManager { | public class DefaultPluginManager extends AbstractPluginManager { | ||||
private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); | |||||
private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); | |||||
protected PluginClasspath pluginClasspath; | protected PluginClasspath pluginClasspath; | ||||
@Override | @Override | ||||
protected ExtensionFinder createExtensionFinder() { | protected ExtensionFinder createExtensionFinder() { | ||||
DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); | |||||
DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); | |||||
addPluginStateListener(extensionFinder); | addPluginStateListener(extensionFinder); | ||||
return extensionFinder; | return extensionFinder; | ||||
} | } | ||||
log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); | log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); | ||||
} | |||||
} | |||||
/** | /** | ||||
* 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 |
*/ | */ | ||||
public class DependencyResolver { | public class DependencyResolver { | ||||
private static final Logger log = LoggerFactory.getLogger(DependencyResolver.class); | |||||
private static final Logger log = LoggerFactory.getLogger(DependencyResolver.class); | |||||
private VersionManager versionManager; | |||||
private VersionManager versionManager; | |||||
private DirectedGraph<String> dependenciesGraph; // the value is 'pluginId' | private DirectedGraph<String> dependenciesGraph; // the value is 'pluginId' | ||||
private DirectedGraph<String> dependentsGraph; // the value is 'pluginId' | private DirectedGraph<String> dependentsGraph; // the value is 'pluginId' | ||||
"' for plugin '" + dependent.getPluginId() + "'"); | "' for plugin '" + dependent.getPluginId() + "'"); | ||||
} | } | ||||
public static class Result { | |||||
public static class Result { | |||||
private boolean cyclicDependency; | |||||
private List<String> notFoundDependencies; // value is "pluginId" | |||||
private boolean cyclicDependency; | |||||
private List<String> notFoundDependencies; // value is "pluginId" | |||||
private List<String> sortedPlugins; // value is "pluginId" | private List<String> sortedPlugins; // value is "pluginId" | ||||
private List<WrongDependencyVersion> wrongVersionDependencies; | private List<WrongDependencyVersion> wrongVersionDependencies; | ||||
return cyclicDependency; | return cyclicDependency; | ||||
} | } | ||||
/** | |||||
* Returns a list with dependencies required that were not found. | |||||
*/ | |||||
/** | |||||
* Returns a list with dependencies required that were not found. | |||||
*/ | |||||
public List<String> getNotFoundDependencies() { | public List<String> getNotFoundDependencies() { | ||||
return notFoundDependencies; | return notFoundDependencies; | ||||
} | } |
@Documented | @Documented | ||||
public @interface Extension { | public @interface Extension { | ||||
int ordinal() default 0; | |||||
int ordinal() default 0; | |||||
} | } |
*/ | */ | ||||
public class LegacyExtensionFinder extends AbstractExtensionFinder { | public class LegacyExtensionFinder extends AbstractExtensionFinder { | ||||
private static final Logger log = LoggerFactory.getLogger(LegacyExtensionFinder.class); | |||||
private static final Logger log = LoggerFactory.getLogger(LegacyExtensionFinder.class); | |||||
public LegacyExtensionFinder(PluginManager pluginManager) { | |||||
public LegacyExtensionFinder(PluginManager pluginManager) { | |||||
super(pluginManager); | super(pluginManager); | ||||
} | |||||
} | |||||
@Override | @Override | ||||
public Map<String, Set<String>> readClasspathStorages() { | public Map<String, Set<String>> readClasspathStorages() { |
} | } | ||||
@Override | @Override | ||||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||||
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) throws PluginException { | ||||
if (FileUtils.isJarFile(pluginPath)) { | if (FileUtils.isJarFile(pluginPath)) { |
private static final Logger log = LoggerFactory.getLogger(PluginClassLoader.class); | private static final Logger log = LoggerFactory.getLogger(PluginClassLoader.class); | ||||
private static final String JAVA_PACKAGE_PREFIX = "java."; | private static final String JAVA_PACKAGE_PREFIX = "java."; | ||||
private static final String PLUGIN_PACKAGE_PREFIX = "org.pf4j."; | |||||
private static final String PLUGIN_PACKAGE_PREFIX = "org.pf4j."; | |||||
private PluginManager pluginManager; | |||||
private PluginDescriptor pluginDescriptor; | |||||
private boolean parentFirst; | |||||
private PluginManager pluginManager; | |||||
private PluginDescriptor pluginDescriptor; | |||||
private boolean parentFirst; | |||||
public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) { | public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) { | ||||
this(pluginManager, pluginDescriptor, parent, false); | this(pluginManager, pluginDescriptor, parent, false); | ||||
* before trying to load the a class through this loader. | * before trying to load the a class through this loader. | ||||
*/ | */ | ||||
public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent, boolean parentFirst) { | public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent, boolean parentFirst) { | ||||
super(new URL[0], parent); | |||||
super(new URL[0], parent); | |||||
this.pluginManager = pluginManager; | |||||
this.pluginDescriptor = pluginDescriptor; | |||||
this.parentFirst = parentFirst; | |||||
} | |||||
this.pluginManager = pluginManager; | |||||
this.pluginDescriptor = pluginDescriptor; | |||||
this.parentFirst = parentFirst; | |||||
} | |||||
@Override | @Override | ||||
public void addURL(URL url) { | |||||
public void addURL(URL url) { | |||||
log.debug("Add '{}'", url); | log.debug("Add '{}'", url); | ||||
super.addURL(url); | |||||
} | |||||
super.addURL(url); | |||||
} | |||||
public void addFile(File file) { | |||||
public void addFile(File file) { | |||||
try { | try { | ||||
addURL(file.getCanonicalFile().toURI().toURL()); | addURL(file.getCanonicalFile().toURI().toURL()); | ||||
} catch (IOException e) { | } catch (IOException e) { | ||||
* via the standard {@link ClassLoader#loadClass(String)} mechanism. | * via the standard {@link ClassLoader#loadClass(String)} mechanism. | ||||
* Use {@link #parentFirst} to change the loading strategy. | * Use {@link #parentFirst} to change the loading strategy. | ||||
*/ | */ | ||||
@Override | |||||
@Override | |||||
public Class<?> loadClass(String className) throws ClassNotFoundException { | public Class<?> loadClass(String className) throws ClassNotFoundException { | ||||
synchronized (getClassLoadingLock(className)) { | synchronized (getClassLoadingLock(className)) { | ||||
// first check whether it's a system class, delegate to the system loader | // first check whether it's a system class, delegate to the system loader |
*/ | */ | ||||
boolean isApplicable(Path pluginPath); | boolean isApplicable(Path pluginPath); | ||||
PluginDescriptor find(Path pluginPath) throws PluginException; | |||||
PluginDescriptor find(Path pluginPath) throws PluginException; | |||||
} | } |
/** | /** | ||||
* Retrieves all resolved plugins (with resolved dependency). | * Retrieves all resolved plugins (with resolved dependency). | ||||
*/ | */ | ||||
List<PluginWrapper> getResolvedPlugins(); | |||||
List<PluginWrapper> getResolvedPlugins(); | |||||
/** | |||||
* Retrieves all unresolved plugins (with unresolved dependency). | |||||
*/ | |||||
List<PluginWrapper> getUnresolvedPlugins(); | |||||
/** | |||||
* Retrieves all unresolved plugins (with unresolved dependency). | |||||
*/ | |||||
List<PluginWrapper> getUnresolvedPlugins(); | |||||
/** | /** | ||||
* Retrieves all started plugins. | * Retrieves all started plugins. | ||||
* @param pluginPath | * @param pluginPath | ||||
* @return the pluginId of the installed plugin or null | * @return the pluginId of the installed plugin or null | ||||
*/ | */ | ||||
String loadPlugin(Path pluginPath); | |||||
String loadPlugin(Path pluginPath); | |||||
/** | /** | ||||
* Start all active plugins. | * Start all active plugins. | ||||
*/ | */ | ||||
boolean deletePlugin(String pluginId); | boolean deletePlugin(String pluginId); | ||||
ClassLoader getPluginClassLoader(String pluginId); | |||||
ClassLoader getPluginClassLoader(String pluginId); | |||||
<T> List<Class<T>> getExtensionClasses(Class<T> type); | <T> List<Class<T>> getExtensionClasses(Class<T> type); | ||||
<T> List<Class<T>> getExtensionClasses(Class<T> type, String pluginId); | <T> List<Class<T>> getExtensionClasses(Class<T> type, String pluginId); | ||||
<T> List<T> getExtensions(Class<T> type); | |||||
<T> List<T> getExtensions(Class<T> type); | |||||
<T> List<T> getExtensions(Class<T> type, String pluginId); | <T> List<T> getExtensions(Class<T> type, String pluginId); | ||||
ExtensionFactory getExtensionFactory(); | ExtensionFactory getExtensionFactory(); | ||||
/** | /** | ||||
* The runtime mode. Must currently be either DEVELOPMENT or DEPLOYMENT. | |||||
*/ | |||||
RuntimeMode getRuntimeMode(); | |||||
* The runtime mode. Must currently be either DEVELOPMENT or DEPLOYMENT. | |||||
*/ | |||||
RuntimeMode getRuntimeMode(); | |||||
/** | /** | ||||
* Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. | * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. |
/** | /** | ||||
* The runtime knows the plugin is there. It knows about the plugin path, the plugin descriptor. | * The runtime knows the plugin is there. It knows about the plugin path, the plugin descriptor. | ||||
*/ | */ | ||||
public static final PluginState CREATED = new PluginState("CREATED"); | |||||
public static final PluginState CREATED = new PluginState("CREATED"); | |||||
/** | /** | ||||
* The plugin cannot be used. | * The plugin cannot be used. | ||||
/** | /** | ||||
* The {@link Plugin#start()} has executed. A started plugin may contribute extensions. | * The {@link Plugin#start()} has executed. A started plugin may contribute extensions. | ||||
*/ | */ | ||||
public static final PluginState STARTED = new PluginState("STARTED"); | |||||
public static final PluginState STARTED = new PluginState("STARTED"); | |||||
/** | /** | ||||
* The {@link Plugin#stop()} has executed. | * The {@link Plugin#stop()} has executed. | ||||
*/ | */ | ||||
public static final PluginState STOPPED = new PluginState("STOPPED"); | public static final PluginState STOPPED = new PluginState("STOPPED"); | ||||
private String status; | |||||
private String status; | |||||
private PluginState(String status) { | |||||
this.status = status; | |||||
} | |||||
private PluginState(String status) { | |||||
this.status = status; | |||||
} | |||||
@Override | @Override | ||||
public boolean equals(Object o) { | public boolean equals(Object o) { | ||||
} | } | ||||
@Override | @Override | ||||
public String toString() { | |||||
return status; | |||||
} | |||||
public String toString() { | |||||
return status; | |||||
} | |||||
} | } |
public class PluginWrapper { | public class PluginWrapper { | ||||
private PluginManager pluginManager; | private PluginManager pluginManager; | ||||
private PluginDescriptor descriptor; | |||||
private Path pluginPath; | |||||
private ClassLoader pluginClassLoader; | |||||
private PluginFactory pluginFactory; | |||||
private PluginState pluginState; | |||||
private RuntimeMode runtimeMode; | |||||
private PluginDescriptor descriptor; | |||||
private Path pluginPath; | |||||
private ClassLoader pluginClassLoader; | |||||
private PluginFactory pluginFactory; | |||||
private PluginState pluginState; | |||||
private RuntimeMode runtimeMode; | |||||
Plugin plugin; // cache | Plugin plugin; // cache | ||||
public PluginWrapper(PluginManager pluginManager, PluginDescriptor descriptor, Path pluginPath, ClassLoader pluginClassLoader) { | |||||
public PluginWrapper(PluginManager pluginManager, PluginDescriptor descriptor, Path pluginPath, ClassLoader pluginClassLoader) { | |||||
this.pluginManager = pluginManager; | this.pluginManager = pluginManager; | ||||
this.descriptor = descriptor; | |||||
this.pluginPath = pluginPath; | |||||
this.pluginClassLoader = pluginClassLoader; | |||||
this.descriptor = descriptor; | |||||
this.pluginPath = pluginPath; | |||||
this.pluginClassLoader = pluginClassLoader; | |||||
pluginState = PluginState.CREATED; | |||||
} | |||||
pluginState = PluginState.CREATED; | |||||
} | |||||
/** | /** | ||||
* Returns the plugin manager. | * Returns the plugin manager. | ||||
* Returns the plugin descriptor. | * Returns the plugin descriptor. | ||||
*/ | */ | ||||
public PluginDescriptor getDescriptor() { | public PluginDescriptor getDescriptor() { | ||||
return descriptor; | |||||
return descriptor; | |||||
} | } | ||||
/** | /** | ||||
* Returns the path of this plugin. | * Returns the path of this plugin. | ||||
*/ | */ | ||||
public Path getPluginPath() { | public Path getPluginPath() { | ||||
return pluginPath; | |||||
return pluginPath; | |||||
} | } | ||||
/** | /** | ||||
* Returns the plugin class loader used to load classes and resources | * Returns the plugin class loader used to load classes and resources | ||||
* for this plug-in. The class loader can be used to directly access | |||||
* plug-in resources and classes. | |||||
*/ | |||||
* for this plug-in. The class loader can be used to directly access | |||||
* plug-in resources and classes. | |||||
*/ | |||||
public ClassLoader getPluginClassLoader() { | public ClassLoader getPluginClassLoader() { | ||||
return pluginClassLoader; | |||||
return pluginClassLoader; | |||||
} | } | ||||
public Plugin getPlugin() { | public Plugin getPlugin() { | ||||
} | } | ||||
return plugin; | return plugin; | ||||
} | |||||
} | |||||
public PluginState getPluginState() { | |||||
return pluginState; | |||||
} | |||||
public PluginState getPluginState() { | |||||
return pluginState; | |||||
} | |||||
public RuntimeMode getRuntimeMode() { | |||||
return runtimeMode; | |||||
} | |||||
public RuntimeMode getRuntimeMode() { | |||||
return runtimeMode; | |||||
} | |||||
/** | /** | ||||
* Shortcut | * Shortcut | ||||
return getDescriptor().getPluginId(); | return getDescriptor().getPluginId(); | ||||
} | } | ||||
@Override | |||||
public int hashCode() { | |||||
final int prime = 31; | |||||
int result = 1; | |||||
result = prime * result + descriptor.getPluginId().hashCode(); | |||||
return result; | |||||
} | |||||
@Override | |||||
public boolean equals(Object obj) { | |||||
if (this == obj) { | |||||
return true; | |||||
} | |||||
if (obj == null) { | |||||
return false; | |||||
} | |||||
if (getClass() != obj.getClass()) { | |||||
return false; | |||||
} | |||||
PluginWrapper other = (PluginWrapper) obj; | |||||
if (!descriptor.getPluginId().equals(other.descriptor.getPluginId())) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "PluginWrapper [descriptor=" + descriptor + ", pluginPath=" + pluginPath + "]"; | |||||
} | |||||
void setPluginState(PluginState pluginState) { | |||||
this.pluginState = pluginState; | |||||
} | |||||
void setRuntimeMode(RuntimeMode runtimeMode) { | |||||
this.runtimeMode = runtimeMode; | |||||
} | |||||
@Override | |||||
public int hashCode() { | |||||
final int prime = 31; | |||||
int result = 1; | |||||
result = prime * result + descriptor.getPluginId().hashCode(); | |||||
return result; | |||||
} | |||||
@Override | |||||
public boolean equals(Object obj) { | |||||
if (this == obj) { | |||||
return true; | |||||
} | |||||
if (obj == null) { | |||||
return false; | |||||
} | |||||
if (getClass() != obj.getClass()) { | |||||
return false; | |||||
} | |||||
PluginWrapper other = (PluginWrapper) obj; | |||||
if (!descriptor.getPluginId().equals(other.descriptor.getPluginId())) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return "PluginWrapper [descriptor=" + descriptor + ", pluginPath=" + pluginPath + "]"; | |||||
} | |||||
void setPluginState(PluginState pluginState) { | |||||
this.pluginState = pluginState; | |||||
} | |||||
void setRuntimeMode(RuntimeMode runtimeMode) { | |||||
this.runtimeMode = runtimeMode; | |||||
} | |||||
void setPluginFactory(PluginFactory pluginFactory) { | void setPluginFactory(PluginFactory pluginFactory) { | ||||
this.pluginFactory = pluginFactory; | this.pluginFactory = pluginFactory; |
*/ | */ | ||||
public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder { | public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder { | ||||
private static final Logger log = LoggerFactory.getLogger(PropertiesPluginDescriptorFinder.class); | |||||
private static final Logger log = LoggerFactory.getLogger(PropertiesPluginDescriptorFinder.class); | |||||
private static final String DEFAULT_PROPERTIES_FILE_NAME = "plugin.properties"; | |||||
private static final String DEFAULT_PROPERTIES_FILE_NAME = "plugin.properties"; | |||||
protected String propertiesFileName; | |||||
protected String propertiesFileName; | |||||
public PropertiesPluginDescriptorFinder() { | |||||
this(DEFAULT_PROPERTIES_FILE_NAME); | |||||
} | |||||
public PropertiesPluginDescriptorFinder() { | |||||
this(DEFAULT_PROPERTIES_FILE_NAME); | |||||
} | |||||
public PropertiesPluginDescriptorFinder(String propertiesFileName) { | |||||
public PropertiesPluginDescriptorFinder(String propertiesFileName) { | |||||
this.propertiesFileName = propertiesFileName; | this.propertiesFileName = propertiesFileName; | ||||
} | |||||
} | |||||
@Override | @Override | ||||
public boolean isApplicable(Path pluginPath) { | public boolean isApplicable(Path pluginPath) { | ||||
} | } | ||||
@Override | @Override | ||||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||||
public PluginDescriptor find(Path pluginPath) throws PluginException { | |||||
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) throws PluginException { | ||||
Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); | Path propertiesPath = getPropertiesPath(pluginPath, propertiesFileName); | ||||
} | } | ||||
protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) throws PluginException { | protected Path getPropertiesPath(Path pluginPath, String propertiesFileName) throws PluginException { | ||||
if (Files.isDirectory(pluginPath)) { | |||||
if (Files.isDirectory(pluginPath)) { | |||||
return pluginPath.resolve(Paths.get(propertiesFileName)); | return pluginPath.resolve(Paths.get(propertiesFileName)); | ||||
} else { | } else { | ||||
// it's a jar file | |||||
// it's a jar file | |||||
try { | try { | ||||
return FileUtils.getPath(pluginPath, propertiesFileName); | return FileUtils.getPath(pluginPath, propertiesFileName); | ||||
} catch (IOException e) { | } catch (IOException e) { |
} | } | ||||
@Override | @Override | ||||
public SourceVersion getSupportedSourceVersion() { | |||||
return SourceVersion.latest(); | |||||
} | |||||
public SourceVersion getSupportedSourceVersion() { | |||||
return SourceVersion.latest(); | |||||
} | |||||
@Override | |||||
public Set<String> getSupportedAnnotationTypes() { | |||||
Set<String> annotationTypes = new HashSet<>(); | |||||
@Override | |||||
public Set<String> getSupportedAnnotationTypes() { | |||||
Set<String> annotationTypes = new HashSet<>(); | |||||
annotationTypes.add(Extension.class.getName()); | annotationTypes.add(Extension.class.getName()); | ||||
return annotationTypes; | return annotationTypes; | ||||
} | |||||
} | |||||
@Override | @Override | ||||
public Set<String> getSupportedOptions() { | public Set<String> getSupportedOptions() { | ||||
} | } | ||||
@Override | @Override | ||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { | |||||
if (roundEnv.processingOver()) { | |||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { | |||||
if (roundEnv.processingOver()) { | |||||
return false; | return false; | ||||
} | } | ||||
info("Processing @%s", Extension.class); | info("Processing @%s", Extension.class); | ||||
for (Element element : roundEnv.getElementsAnnotatedWith(Extension.class)) { | |||||
for (Element element : roundEnv.getElementsAnnotatedWith(Extension.class)) { | |||||
// check if @Extension is put on class and not on method or constructor | // check if @Extension is put on class and not on method or constructor | ||||
if (!(element instanceof TypeElement)) { | if (!(element instanceof TypeElement)) { | ||||
continue; | |||||
} | |||||
continue; | |||||
} | |||||
// check if class extends/implements an extension point | // check if class extends/implements an extension point | ||||
if (!isExtension(element.asType())) { | if (!isExtension(element.asType())) { | ||||
// write extensions | // write extensions | ||||
storage.write(extensions); | storage.write(extensions); | ||||
return false; | |||||
} | |||||
return false; | |||||
} | |||||
public ProcessingEnvironment getProcessingEnvironment() { | public ProcessingEnvironment getProcessingEnvironment() { | ||||
return processingEnv; | return processingEnv; |
*/ | */ | ||||
public void addVertex(V vertex) { | public void addVertex(V vertex) { | ||||
if (containsVertex(vertex)) { | if (containsVertex(vertex)) { | ||||
return; | |||||
return; | |||||
} | } | ||||
neighbors.put(vertex, new ArrayList<V>()); | neighbors.put(vertex, new ArrayList<V>()); | ||||
public Map<V, Integer> outDegree() { | public Map<V, Integer> outDegree() { | ||||
Map<V, Integer> result = new HashMap<>(); | Map<V, Integer> result = new HashMap<>(); | ||||
for (V vertex : neighbors.keySet()) { | for (V vertex : neighbors.keySet()) { | ||||
result.put(vertex, neighbors.get(vertex).size()); | |||||
result.put(vertex, neighbors.get(vertex).size()); | |||||
} | } | ||||
return result; | return result; | ||||
public Map<V, Integer> inDegree() { | public Map<V, Integer> inDegree() { | ||||
Map<V, Integer> result = new HashMap<>(); | Map<V, Integer> result = new HashMap<>(); | ||||
for (V vertex : neighbors.keySet()) { | for (V vertex : neighbors.keySet()) { | ||||
result.put(vertex, 0); // all in-degrees are 0 | |||||
result.put(vertex, 0); // all in-degrees are 0 | |||||
} | } | ||||
for (V from : neighbors.keySet()) { | for (V from : neighbors.keySet()) { | ||||
for (V to : neighbors.get(from)) { | for (V to : neighbors.get(from)) { | ||||
Stack<V> zeroVertices = new Stack<>(); // stack as good as any here | Stack<V> zeroVertices = new Stack<>(); // stack as good as any here | ||||
for (V v : degree.keySet()) { | for (V v : degree.keySet()) { | ||||
if (degree.get(v) == 0) { | if (degree.get(v) == 0) { | ||||
zeroVertices.push(v); | |||||
zeroVertices.push(v); | |||||
} | } | ||||
} | } | ||||
degree.put(neighbor, degree.get(neighbor) - 1); | degree.put(neighbor, degree.get(neighbor) - 1); | ||||
// remember any vertices that now have zero in-degree | // remember any vertices that now have zero in-degree | ||||
if (degree.get(neighbor) == 0) { | if (degree.get(neighbor) == 0) { | ||||
zeroVertices.push(neighbor); | |||||
zeroVertices.push(neighbor); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// check that we have used the entire graph (if not, there was a cycle) | // check that we have used the entire graph (if not, there was a cycle) | ||||
if (result.size() != neighbors.size()) { | if (result.size() != neighbors.size()) { | ||||
return null; | |||||
return null; | |||||
} | } | ||||
return result; | return result; | ||||
* Report (as a List) the reverse topological sort of the vertices; null for no such sort. | * Report (as a List) the reverse topological sort of the vertices; null for no such sort. | ||||
*/ | */ | ||||
public List<V> reverseTopologicalSort() { | public List<V> reverseTopologicalSort() { | ||||
List<V> list = topologicalSort(); | |||||
if (list == null) { | |||||
return null; | |||||
} | |||||
List<V> list = topologicalSort(); | |||||
if (list == null) { | |||||
return null; | |||||
} | |||||
Collections.reverse(list); | |||||
Collections.reverse(list); | |||||
return list; | |||||
return list; | |||||
} | } | ||||
/** | /** | ||||
public String toString() { | public String toString() { | ||||
StringBuffer sb = new StringBuffer(); | StringBuffer sb = new StringBuffer(); | ||||
for (V vertex : neighbors.keySet()) { | for (V vertex : neighbors.keySet()) { | ||||
sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); | |||||
sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); | |||||
} | } | ||||
return sb.toString(); | return sb.toString(); |
*/ | */ | ||||
public class DirectoryFileFilter implements FileFilter { | public class DirectoryFileFilter implements FileFilter { | ||||
@Override | |||||
@Override | |||||
public boolean accept(File file) { | public boolean accept(File file) { | ||||
return file.isDirectory(); | return file.isDirectory(); | ||||
} | } |
public static List<String> readLines(Path path, boolean ignoreComments) throws IOException { | public static List<String> readLines(Path path, boolean ignoreComments) throws IOException { | ||||
File file = path.toFile(); | File file = path.toFile(); | ||||
if (!file.exists() || !file.isFile()) { | |||||
return new ArrayList<>(); | |||||
} | |||||
if (!file.exists() || !file.isFile()) { | |||||
return new ArrayList<>(); | |||||
} | |||||
List<String> lines = new ArrayList<>(); | |||||
List<String> lines = new ArrayList<>(); | |||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) { | |||||
String line; | |||||
while ((line = reader.readLine()) != null) { | |||||
if (ignoreComments && !line.startsWith("#") && !lines.contains(line)) { | |||||
lines.add(line); | |||||
} | |||||
} | |||||
} | |||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) { | |||||
String line; | |||||
while ((line = reader.readLine()) != null) { | |||||
if (ignoreComments && !line.startsWith("#") && !lines.contains(line)) { | |||||
lines.add(line); | |||||
} | |||||
} | |||||
} | |||||
return lines; | |||||
} | |||||
return lines; | |||||
} | |||||
public static void writeLines(Collection<String> lines, File file) throws IOException { | public static void writeLines(Collection<String> lines, File file) throws IOException { | ||||
Files.write(file.toPath(), lines, StandardCharsets.UTF_8); | Files.write(file.toPath(), lines, StandardCharsets.UTF_8); | ||||
} | } | ||||
/** | /** | ||||
* Delete a file or recursively delete a folder, do not follow symlinks. | |||||
* | |||||
* @param path the file or folder to delete | |||||
* @throws IOException if something goes wrong | |||||
*/ | |||||
* Delete a file or recursively delete a folder, do not follow symlinks. | |||||
* | |||||
* @param path the file or folder to delete | |||||
* @throws IOException if something goes wrong | |||||
*/ | |||||
public static void delete(Path path) throws IOException { | public static void delete(Path path) throws IOException { | ||||
Files.walkFileTree(path, new SimpleFileVisitor<Path>() { | Files.walkFileTree(path, new SimpleFileVisitor<Path>() { | ||||
} | } | ||||
}); | }); | ||||
} | |||||
} | |||||
public static List<File> getJars(Path folder) { | |||||
List<File> bucket = new ArrayList<>(); | |||||
getJars(bucket, folder); | |||||
public static List<File> getJars(Path folder) { | |||||
List<File> bucket = new ArrayList<>(); | |||||
getJars(bucket, folder); | |||||
return bucket; | |||||
return bucket; | |||||
} | } | ||||
private static void getJars(final List<File> bucket, Path folder) { | private static void getJars(final List<File> bucket, Path folder) { |
*/ | */ | ||||
public class HiddenFilter implements FileFilter { | public class HiddenFilter implements FileFilter { | ||||
@Override | |||||
public boolean accept(File file) { | |||||
return file.isHidden(); | |||||
} | |||||
@Override | |||||
public boolean accept(File file) { | |||||
return file.isHidden(); | |||||
} | |||||
} | } |
*/ | */ | ||||
public class StringUtils { | public class StringUtils { | ||||
public static boolean isNullOrEmpty(String str) { | |||||
return (str == null) || str.isEmpty(); | |||||
} | |||||
public static boolean isNullOrEmpty(String str) { | |||||
return (str == null) || str.isEmpty(); | |||||
} | |||||
public static boolean isNotNullOrEmpty(String str) { | public static boolean isNotNullOrEmpty(String str) { | ||||
return !isNullOrEmpty(str); | return !isNullOrEmpty(str); |
*/ | */ | ||||
public class Unzip { | public class Unzip { | ||||
private static final Logger log = LoggerFactory.getLogger(Unzip.class); | |||||
private static final Logger log = LoggerFactory.getLogger(Unzip.class); | |||||
/** | /** | ||||
* Holds the destination directory. | * Holds the destination directory. | ||||
FileUtils.delete(destination.toPath()); | FileUtils.delete(destination.toPath()); | ||||
} | } | ||||
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(source))) { | |||||
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(source))) { | |||||
ZipEntry zipEntry; | ZipEntry zipEntry; | ||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) { | while ((zipEntry = zipInputStream.getNextEntry()) != null) { | ||||
try { | try { |