Skip to content

Commit

Permalink
Merge pull request #23 from vania-pooh/master
Browse files Browse the repository at this point in the history
Added plugin development specific implementations
  • Loading branch information
robot-bucket committed Jan 6, 2015
2 parents 3b186ae + 2da61aa commit a521825
Show file tree
Hide file tree
Showing 37 changed files with 836 additions and 206 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,19 @@ An extension point can be an ordinary class (i.e. class or interface) or an anno

## Internals
Internally plugin engine is based on the following interfaces:
* **PluginsProvider** - return a list of paths to be considered as plugin files or directories.
* **ManifestReader** - reads plugin manifest and returns an object with respective field values. Overriding default implementation can be used to change field names.
* **DependencyChecker** - uses data from manifest fields and checks that all required dependencies are present and no conflicting dependencies are present. To compare plugin versions an implementation of **VersionComparator** is used.
* **ClassesScanner** - scans **plugin.jar** file and searches for classes implementing extension points. Any class loading logic should be implemented here.
* **ResourcesScanner** - the same as **ClassesScanner** but for resource files.
All enumerated interfaces have default implementations but you can easily replace them with your own:
```java
File aDirectoryWithPlugins = new File("some/directory");
PluginRegistry pluginRegistry = PluginLoader
.withPluginDirectory(aDirectoryWithPlugins)
.withPluginsProvider(new MyCustomPluginsProvider())
.withDependencyChecker(new MyCustomDependencyChecker())
.withClassesScanner(new MyCustomClassesScanner())
.withResourcesScanner(new MyCustomResourcesScanner())
.load();
```
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@

<dependencyManagement>
<dependencies>
<dependency>
<groupId>ru.meridor.stecker</groupId>
<artifactId>stecker-plugin-loader</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
1 change: 0 additions & 1 deletion stecker-plugin-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
<dependency>
<groupId>ru.meridor.stecker</groupId>
<artifactId>stecker-plugin-loader</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.*;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.jar.JarArchiver;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package ru.meridor.stecker;

import ru.meridor.stecker.impl.*;
import ru.meridor.stecker.impl.DefaultClassesScanner;
import ru.meridor.stecker.impl.DefaultDependencyChecker;
import ru.meridor.stecker.impl.DefaultManifestReader;
import ru.meridor.stecker.impl.DefaultPluginsProvider;
import ru.meridor.stecker.impl.DefaultResourcesScanner;
import ru.meridor.stecker.impl.PluginRegistryContainer;
import ru.meridor.stecker.interfaces.ClassesScanner;
import ru.meridor.stecker.interfaces.DependencyChecker;
import ru.meridor.stecker.interfaces.ManifestReader;
import ru.meridor.stecker.interfaces.PluginsProvider;
import ru.meridor.stecker.interfaces.ResourcesScanner;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -25,11 +27,13 @@ public class PluginLoader {

private static final String DEFAULT_FILE_GLOB = "**/*.jar";
private String fileGlob = DEFAULT_FILE_GLOB;
private static final String DEFAULT_CACHE_DIRECTORY = ".cache";
private final Path pluginDirectory;
public static final String DEFAULT_CACHE_DIRECTORY = ".cache";
private final Path pluginsDirectory;
private final List<Class> extensionPoints = new ArrayList<>();
private Path cacheDirectory;

private PluginsProvider pluginsProvider;

private ManifestReader manifestReader;

private DependencyChecker dependencyChecker;
Expand All @@ -40,22 +44,22 @@ public class PluginLoader {

private String[] resourcesPatterns = new String[0];

private PluginLoader(Path pluginDirectory) {
this.pluginDirectory = pluginDirectory;
private PluginLoader(Path pluginsDirectory) {
this.pluginsDirectory = pluginsDirectory;
}

/**
* Define the directory to search for plugins
*
* @param pluginDirectory directory containing plugins
* @param pluginsDirectory directory containing plugins
* @return this
* @throws PluginException
*/
public static PluginLoader withPluginDirectory(Path pluginDirectory) throws PluginException {
if (pluginDirectory == null) {
throw new PluginException("Plugin directory can't be null");
public static PluginLoader withPluginDirectory(Path pluginsDirectory) throws PluginException {
if (pluginsDirectory == null) {
throw new PluginException("Plugins directory can't be null");
}
return new PluginLoader(pluginDirectory);
return new PluginLoader(pluginsDirectory);
}

/**
Expand All @@ -69,6 +73,17 @@ public PluginLoader withFileGlob(String fileGlob) {
return this;
}

/**
* Define plugins provider instance to be used
*
* @param pluginsProvider plugins provider instance
* @return this
*/
public PluginLoader withPluginsProvider(PluginsProvider pluginsProvider) {
this.pluginsProvider = pluginsProvider;
return this;
}

/**
* Define cache directory used to extract plugin files
*
Expand Down Expand Up @@ -156,8 +171,8 @@ public PluginLoader withResourcesPatterns(String... resourcesPatterns) {
*
* @return plugin directory
*/
public Path getPluginDirectory() {
return pluginDirectory;
public Path getPluginsDirectory() {
return pluginsDirectory;
}

/**
Expand All @@ -169,14 +184,23 @@ public String getFileGlob() {
return fileGlob;
}

/**
* Returns plugins provider instance used during loader run
*
* @return plugins provider instance
*/
public PluginsProvider getPluginsProvider() {
return (pluginsProvider != null) ? pluginsProvider : new DefaultPluginsProvider(getFileGlob());
}

/**
* Returns the cache directory to which we unpack plugin contents
*
* @return cache directory
*/
public Path getCacheDirectory() {
return (cacheDirectory != null) ?
cacheDirectory : pluginDirectory.resolve(DEFAULT_CACHE_DIRECTORY);
cacheDirectory : pluginsDirectory.resolve(DEFAULT_CACHE_DIRECTORY);
}

/**
Expand Down Expand Up @@ -244,7 +268,7 @@ public List<Class> getExtensionPoints() {
*/
public PluginRegistry load() throws PluginException {

List<Path> pluginFiles = getPluginFiles();
List<Path> pluginFiles = getPluginsProvider().provide(getPluginsDirectory());

PluginRegistry pluginRegistry = new PluginRegistryContainer();

Expand Down Expand Up @@ -275,19 +299,4 @@ public PluginRegistry load() throws PluginException {
return pluginRegistry;
}

private List<Path> getPluginFiles() throws PluginException {
try {
Path pluginDirectory = getPluginDirectory();
PathMatcher pathMatcher = FileSystems
.getDefault()
.getPathMatcher("glob:" + fileGlob);
return Files.list(pluginDirectory)
.filter(pathMatcher::matches)
.collect(Collectors.toList());
} catch (IOException e) {
throw new PluginException(e);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,29 @@
*/
public class ResourcesWatcher implements Closeable {

private static final String THREAD_NAME = "Stecker Resources Watcher";

private volatile List<ResourceChangedHandler> handlers = new ArrayList<>();

private volatile boolean isStarted;

private Thread watcherThread;

public ResourcesWatcher(List<Path> resources) {
this.watcherThread = getWatcherThread(resources);
}

private Thread getWatcherThread(List<Path> resources) {
return isMacOs() ? getScanningWatcherThread(resources) : getWatchServiceWatcherThread(resources);
}

private static boolean isMacOs() {
//WatchService doesn't work correctly on MacOS
return System.getProperty("os.name").toLowerCase().contains("mac");
}

private Thread getWatchServiceWatcherThread(List<Path> resources) {
return new Thread() {
Thread thread = new Thread() {
@Override
public void run() {
super.run();
Expand Down Expand Up @@ -81,7 +83,7 @@ public void run() {
handler.onResourceChanged(filePath);
}
});
if (!key.reset()){
if (!key.reset()) {
break;
}
}
Expand All @@ -90,11 +92,14 @@ public void run() {
}
}
};
thread.setName(THREAD_NAME);
thread.setDaemon(true);
return thread;
}

private Thread getScanningWatcherThread(List<Path> resources) {
final long SCAN_DELAY = 1000;
return new Thread(){
Thread thread = new Thread() {
@Override
public void run() {
super.run();
Expand All @@ -103,13 +108,14 @@ public void run() {
for (Path resource : resources) {
lastModifiedTimes.put(resource, PluginUtils.getLastModificationTime(resource));
}

while (true) {
try {
for (Path resource : resources) {
FileTime previousLastModifiedTime = lastModifiedTimes.get(resource);
FileTime currentLastModifiedTime = PluginUtils.getLastModificationTime(resource);
if (currentLastModifiedTime.compareTo(previousLastModifiedTime) > 0) {
lastModifiedTimes.put(resource, currentLastModifiedTime);
for (ResourceChangedHandler handler : handlers) {
handler.onResourceChanged(resource);
}
Expand All @@ -120,14 +126,17 @@ public void run() {
break;
}
}

} catch (IOException e) {
throw new IllegalStateException(e);
}
}
};
thread.setName(THREAD_NAME);
thread.setDaemon(true);
return thread;
}

public void addChangedHandler(ResourceChangedHandler handler) {
handlers.add(handler);
}
Expand All @@ -138,9 +147,12 @@ public void start() {
}

public void stop() {
watcherThread.interrupt();
if (isStarted) {
watcherThread.interrupt();
isStarted = false;
}
}

public void await(Path resource) throws InterruptedException {
if (!isStarted) {
start();
Expand All @@ -152,8 +164,9 @@ public void await(Path resource) throws InterruptedException {
}
});
countDownLatch.await();
stop();
}

public void await() throws InterruptedException {
await(null);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ru.meridor.stecker.dev;

import ru.meridor.stecker.PluginException;
import ru.meridor.stecker.impl.PluginUtils;

import java.nio.file.Path;

public class BuildToolClassLoaderFactory {

public static ClassLoader getClassLoader(Path baseDirectory, BuildToolType buildToolType) throws PluginException {
switch (buildToolType) {
default:
case MAVEN: {
return PluginUtils.getClassLoader(
getClassesPath(baseDirectory, buildToolType),
getDependenciesPath(baseDirectory, buildToolType)
);
}
}
}

public static Path getClassesPath(Path baseDirectory, BuildToolType buildToolType) {
switch (buildToolType) {
default:
case MAVEN:
return getTargetDirectory(baseDirectory).resolve("classes");
}
}

public static Path getDependenciesPath(Path baseDirectory, BuildToolType buildToolType) {
switch (buildToolType) {
default:
case MAVEN:
return getTargetDirectory(baseDirectory).resolve("plugin-generator").resolve("data").resolve("lib");
}
}

private static Path getTargetDirectory(Path baseDirectory) {
return baseDirectory.resolve("target");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ru.meridor.stecker.dev;

public enum BuildToolType {

//TODO: we may want to add other tools like Gradle and SBT
MAVEN

}
Loading

0 comments on commit a521825

Please sign in to comment.