-
Notifications
You must be signed in to change notification settings - Fork 5
Plugin API
Plugin API provides a way for developers to extend CompaJ system with new features. There are two ways to load plugins:
- as packaged jar libs
- as raw source code
You can add Plugin API as a dependency in your maven, gradle, e.t.c project:
<dependency>
<artifactId>plugin-api</artifactId>
<groupId>tech.hiddenproject.compaj</groupId>
// Use provided scope to exclude plugin-api from packaged jar
// It will be provided by CompaJ at runtime
<scope>provided</scope>
<version>0.0.3</version>
</dependency>
To use Plugin API without build system just download latest release jar at releases page and include it in your project.
Your plugin can contain provider class which will be created during loading process. Such class must meet the following requirements:
- have constructor with no arguments
- implement
tech.hiddenproject.compaj.plugin.api.CompaJPlugin
interface
CompaJ creates instances of all provider classes, so you can run any actions in constructor.
public class MyPlugin implements CompaJPlugin {
private static final Logger LOGGER = LoggerFactory.getLogger(MyPlugin.class);
public MyPlugin() {
LOGGER.info("Plugin started: {}", this.getClass());
}
}
Providers can export other classes or themselves to CompaJ. It means that such classes will be auto imported in CompaJ environment and will be available for users by their names without explicit imports.
To export other classes use @Exports(Class[])
. Assume we have some MyClass
:
public class MyClass {
public static double pI() {
return Math.PI;
}
}
To export this class into CompaJ:
@Exports(MyClass.class)
public class MyPlugin implements CompaJPlugin {
private static final Logger LOGGER = LoggerFactory.getLogger(MyPlugin.class);
public MyPlugin() {
LOGGER.info("Plugin started: {}", this.getClass());
}
}
Now MyClass
will be available in CompaJ:
> myClass = :MyClass
tech.hiddenproject.plugin.example.MyClass@56de6d6b
> MyClass.pI()
3.141592653589793
Providers can export themselves with @ExportSelf
:
@ExportsSelf
public class MyPlugin implements CompaJPlugin {
private static final Logger LOGGER = LoggerFactory.getLogger(MyPlugin.class);
public MyPlugin() {
LOGGER.info("Plugin started: {}", this.getClass());
}
public static int myFunc() {
return 5;
}
}
Now MyPlugin
will be available in CompaJ:
> MyPlugin.myFunc()
5
CompaJ uses standard ServiceLoader
from JDK. So to make your plugin visible you need to:
- Create resource folder if it doesn't exists
- Create META-INF folder
- Create services folder inside META-INF
- Create file with name
tech.hiddenproject.compaj.plugin.api.CompaJPlugin
and no extension - Place all canonical (package + class name) provider (classes implement CompaJPlugin) class names on each new line
Result structure should look like:
├── resources
│ ├── META-INF
│ │ ├── tech.hiddenproject.compaj.plugin.api.CompaJPlugin
│
File content should look like this:
tech.hiddenproject.plugin.example.MyPlugin
tech.hiddenproject.plugin.example.AnotherPlugin
Packaged plugins can be loaded as external libraries. Read about libraries loading here.
Raw plugins are raw source code files. This way is preferable, cause users can see source code and check it for danger code.
Raw plugins are Groovy/Java/CompaJ source code which will be compiled by CompaJ on startup. It has equal requirements as Java code:
- each package is a different folder
- file name must be same as class name
- manual class imports required
But unlike java programs CompaJ plugins can have many top-level classes in one file.
Plugins are located in ${user.home}/CompaJ/plugins/
. Each plugin must be located inside it's own folder.
Assume we have package tech.hiddenproject
. Plugin structure looks like this:
├── ${user.home}
│ ├── CompaJ
│ │ ├── plugins
│ │ │ ├── tech
│ │ │ │ ├── hiddenproject
│ │ │ │ │ ├── Main.cjp //package tech.hiddenproject
│ │ │ │ │ │ ├── myplugin
│ │ │ │ │ │ │ ├── SomeClass.cjp // package tech.hiddenproject.myplugin
All plugins must have class called Main
. It must be located in root plugin folder as Main.cjp
file.
Main class can have public static void main(String... args)
function (as standard java program). It will be called on plugin loading.
You also can import all other classes that should be imported in CompaJ, so users can use them without explicit import.
package pl
import pl.TestPlugin // users can use just TestPlugin, without pl. prefix
class Main {
static String s = "HI";
private static String s;
public static void main(args) { println s }
public static int q(args) {
MyPlugin.q(args)
}
}
class MyPlugin {
public static int q(arg) {
arg * arg
}
}
To interact with other plugins and main CompaJ components you can use EventPublisher
. All communication is based on Pub-Sub pattern where components can send events with payloads to some topics and subscribe on that events.
According to https://issues.apache.org/jira/browse/GROOVY-3010, Groovy ignores private modifier on class fields. In future it will be fixed with https://github.com/stansonhealth/ast-framework, but for now you can use inner private classes to encapsulate fields.
Plugin API for Desktop app is still in development. For now CompaJ allow developers to add new menus on menu bar.