diff --git a/core/.classpath b/core/.classpath
index 10ef69c68..deeb71e4f 100644
--- a/core/.classpath
+++ b/core/.classpath
@@ -25,5 +25,6 @@
+
diff --git a/core/lib/jspf.core-1.0.2.jar b/core/lib/jspf.core-1.0.2.jar
new file mode 100644
index 000000000..02dec5bc7
Binary files /dev/null and b/core/lib/jspf.core-1.0.2.jar differ
diff --git a/core/src/net/sf/openrocket/plugin/AbstractService.java b/core/src/net/sf/openrocket/plugin/AbstractService.java
new file mode 100644
index 000000000..027196461
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/AbstractService.java
@@ -0,0 +1,45 @@
+package net.sf.openrocket.plugin;
+
+import java.util.Collections;
+import java.util.List;
+
+import net.sf.openrocket.util.BugException;
+
+/**
+ * An abstract service implementation that returns plugins of type P.
+ *
+ * @param
the plugin type that this service returns.
+ * @author Sampo Niskanen
+ */
+public abstract class AbstractService implements Service {
+
+ private final Class
type;
+
+ protected AbstractService(Class
type) {
+ this.type = type;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public List getPlugins(Class e, Object... args) {
+
+ if (e != type) {
+ return Collections.emptyList();
+ }
+
+ List plugins = getPlugins(args);
+
+ // Check list content types to avoid mysterious bugs later on
+ for (P p : plugins) {
+ if (!type.isInstance(p)) {
+ throw new BugException("Requesting plugins of type " + type + " but received " +
+ ((p != null) ? p.getClass() : "null"));
+ }
+ }
+
+ return (List) plugins;
+ }
+
+ protected abstract List getPlugins(Object... args);
+
+}
diff --git a/core/src/net/sf/openrocket/plugin/JSPFPluginFactory.java b/core/src/net/sf/openrocket/plugin/JSPFPluginFactory.java
new file mode 100644
index 000000000..6b598b692
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/JSPFPluginFactory.java
@@ -0,0 +1,50 @@
+package net.sf.openrocket.plugin;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.openrocket.util.BugException;
+import net.xeoh.plugins.base.Plugin;
+import net.xeoh.plugins.base.PluginManager;
+import net.xeoh.plugins.base.impl.PluginManagerFactory;
+import net.xeoh.plugins.base.util.JSPFProperties;
+import net.xeoh.plugins.base.util.PluginManagerUtil;
+
+public class JSPFPluginFactory implements PluginFactory {
+
+ private final PluginManager pluginManager;
+
+ public JSPFPluginFactory() {
+
+ final JSPFProperties props = new JSPFProperties();
+
+ // props.setProperty(PluginManager.class, "cache.enabled", "true");
+ // props.setProperty(PluginManager.class, "cache.mode", "weak"); //optional
+ // props.setProperty(PluginManager.class, "cache.file", "jspf.cache");
+
+ try {
+ pluginManager = PluginManagerFactory.createPluginManager(props);
+ pluginManager.addPluginsFrom(new URI("classpath://*"));
+ } catch (URISyntaxException e) {
+ throw new BugException(e);
+ }
+ }
+
+ @Override
+ public List getPlugins(Class e, Object... args) {
+
+ List plugins = new ArrayList();
+
+ PluginManagerUtil pluginManagerUtil = new PluginManagerUtil(pluginManager);
+ plugins.addAll(pluginManagerUtil.getPlugins(e));
+
+ for (Service s : pluginManagerUtil.getPlugins(Service.class)) {
+ plugins.addAll(s.getPlugins(e, args));
+ }
+
+ return plugins;
+
+ }
+}
diff --git a/core/src/net/sf/openrocket/plugin/PluginFactory.java b/core/src/net/sf/openrocket/plugin/PluginFactory.java
new file mode 100644
index 000000000..babf01da2
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/PluginFactory.java
@@ -0,0 +1,11 @@
+package net.sf.openrocket.plugin;
+
+import java.util.List;
+
+import net.xeoh.plugins.base.Plugin;
+
+public interface PluginFactory {
+
+ public List getPlugins(Class e, Object... args);
+
+}
diff --git a/core/src/net/sf/openrocket/plugin/Service.java b/core/src/net/sf/openrocket/plugin/Service.java
new file mode 100644
index 000000000..f19c6b161
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/Service.java
@@ -0,0 +1,13 @@
+package net.sf.openrocket.plugin;
+
+import java.util.List;
+
+import net.xeoh.plugins.base.Plugin;
+
+public interface Service extends Plugin {
+
+
+ public List getPlugins(Class e, Object... args);
+
+
+}
diff --git a/core/src/net/sf/openrocket/plugin/example/ExampleMain.java b/core/src/net/sf/openrocket/plugin/example/ExampleMain.java
new file mode 100644
index 000000000..c45afee29
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/example/ExampleMain.java
@@ -0,0 +1,21 @@
+package net.sf.openrocket.plugin.example;
+
+import net.sf.openrocket.plugin.JSPFPluginFactory;
+import net.sf.openrocket.plugin.PluginFactory;
+
+public class ExampleMain {
+
+
+ public static void main(String[] args) {
+ PluginFactory factory = new JSPFPluginFactory();
+
+ System.out.println("Plugins:");
+ System.out.println("---------");
+ for (ExamplePluginInterface plugin : factory.getPlugins(ExamplePluginInterface.class)) {
+ plugin.print();
+ }
+ System.out.println("---------");
+ }
+
+
+}
diff --git a/core/src/net/sf/openrocket/plugin/example/ExamplePlugin.java b/core/src/net/sf/openrocket/plugin/example/ExamplePlugin.java
new file mode 100644
index 000000000..4579f9e3d
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/example/ExamplePlugin.java
@@ -0,0 +1,17 @@
+package net.sf.openrocket.plugin.example;
+
+
+public class ExamplePlugin implements ExamplePluginInterface {
+
+ private final String str;
+
+ public ExamplePlugin(String str) {
+ this.str = str;
+ }
+
+ @Override
+ public void print() {
+ System.out.println("ExamplePlugin: " + str);
+ }
+
+}
diff --git a/core/src/net/sf/openrocket/plugin/example/ExamplePluginInterface.java b/core/src/net/sf/openrocket/plugin/example/ExamplePluginInterface.java
new file mode 100644
index 000000000..ddf157dc6
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/example/ExamplePluginInterface.java
@@ -0,0 +1,9 @@
+package net.sf.openrocket.plugin.example;
+
+import net.xeoh.plugins.base.Plugin;
+
+public interface ExamplePluginInterface extends Plugin {
+
+ public void print();
+
+}
diff --git a/core/src/net/sf/openrocket/plugin/example/ExampleService.java b/core/src/net/sf/openrocket/plugin/example/ExampleService.java
new file mode 100644
index 000000000..9960bcdab
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/example/ExampleService.java
@@ -0,0 +1,24 @@
+package net.sf.openrocket.plugin.example;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.openrocket.plugin.AbstractService;
+import net.xeoh.plugins.base.annotations.PluginImplementation;
+
+@PluginImplementation
+public class ExampleService extends AbstractService {
+
+ protected ExampleService() {
+ super(ExamplePluginInterface.class);
+ }
+
+ @Override
+ protected List getPlugins(Object... args) {
+ List plugins = new ArrayList();
+ plugins.add(new ExamplePlugin("a"));
+ plugins.add(new ExamplePlugin("b"));
+ return plugins;
+ }
+
+}
diff --git a/core/src/net/sf/openrocket/plugin/example/ExampleSingletonPlugin.java b/core/src/net/sf/openrocket/plugin/example/ExampleSingletonPlugin.java
new file mode 100644
index 000000000..aa015180e
--- /dev/null
+++ b/core/src/net/sf/openrocket/plugin/example/ExampleSingletonPlugin.java
@@ -0,0 +1,13 @@
+package net.sf.openrocket.plugin.example;
+
+import net.xeoh.plugins.base.annotations.PluginImplementation;
+
+@PluginImplementation
+public class ExampleSingletonPlugin implements ExamplePluginInterface {
+
+ @Override
+ public void print() {
+ System.out.println("ExampleSingletonPlugin");
+ }
+
+}