|
We try to explain by example here, so adopt the strategies to your scenario accordingly. More Examples@PluginImplementation(author="Ralf Biedert", isDisabled=false, configurationFile="config.properties")
public class BigPluginImpl implements BigPlugin {
@InjectPlugin
public CoolPlugin coolPlugin;
@InjectPlugin
public PluginConfigurationFacade configFacade;
@InjectPlugin
public InformationBroker broker;
@Thread
public void backgroundTask() {
while(true) broker.publish(new StringItem("item:uri", "value"));
}
@Init
public void init() {
int port = configFacade.getInt(BigPlugin.class, "server.port", 12345);
callSetup()
}
@Shutdown
public void shutdown() {
sayGoodbye()
}
@Capabilities
public String[] caps() {
return new String[] {"mimetype/big", "functionality:example"};
}
@PluginLoaded
public void newPlugin(RemoteAPI plugin) {
System.out.printf("Detected new plugin " + plugin);
}
}
Exporting and Importing Plugins over the NetExporting and importing is really easy. And the best thing is: you don't have to hassle with host names or port numbers. // Get exporter, this one uses Lipe
RemoteAPI remote = pm.getPlugin(RemoteAPILipe.class)
// after this line returns the plugin is exported
remote.exportPlugin(myPlugin);
// import it again over the network, detect its location automatically
MyPlugin myPluginNetwork = remote.getRemoteProxy(new URI("discover://any"), MyPlugin.class);
Enable CachingThe following code should give you faster loading times after the first startup: 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");
PluginManager pm = PluginManagerFactory.createPluginManager(props);
Recommended LayoutWe usually create a package of the structure org.domain.applicationname. Within this folder you would find a single application main class, containing, amongst others, this code: PluginManager pm = PluginManagerFactory.createPluginManager();
pm.addPluginsFrom(new URI("classpath://*"));These lines will create a new plugin manager instance and tell it to load all classes it sees within the classpath. Except applets and some special cases this should work just fine. Of course you might want to add other locations as well (for example a special plugin-directory). Below the main package we ususally create a subpackage named plugins or services. Inside this package go other subpackages, one for each plugin: - plugins
- storage
- uriservice
- systemapi
Inside a single plugin directory, you'd now place the interface of the plugin. How you design your interface is totally up to you, be we'd strongly recommend to keep it small. You could place, for example, inside the package org.domain.applicationname.plugins.systemapi the following interface file: public interface SystemAPI extends Plugin {
/** Tells the system to open the given url using the default application */
public void openURL(URL href);
}Enums and other interfaces could go here as well. The only thing remaining would be the interface's implementation. This usually goes into an impl subfolder, one for each plugin:
In there one SystemAPIImpl.java file might look like that: @PluginImplementation
public class SystemAPIImpl implements SystemAPI {
@Init
public boolean init() {
return getOS().equals("OSX");
}
public void openURL(URL href) {
doSomething(href);
}
}
Plugin Design PracticesThese hints are by no means required. Technically, for small projects, you should not encounter any problems if you develop all plugins by yourself from one big classpath. However, if you intend to publish you plugins for general usage, consider these points: - A well designed plugin consists of a small and self-disclosing interface. In the interface try to use, if possible, rather standard java types than specialized classes or other interfaces.
- Instead of building one large interface, create several small ones.
- As a rule of thumb: Every plugin should be self contained. This means the plugin must be error-free-inspectable by other users who only have the core plugin available.
To archive this the plugin-jar should contain all interface-definitions it depends on (even foreign ones) and all foreign libraries it uses (unpack the remote.xmlrpc-jar to see what we mean). If you do not follow this rule, this is what can happen:
When an incomplete plugin is deployed into a plugin directory it might get loaded at a random point in time. If a class in your plugin depends on an entity that is not in the classpath (and not in your plugin, because you forgot to put it in there), its creation will fail due to a ClassNotFoundException. If however, you put all dependent interfaces into its jar, the framework will be able to load it properly and inspect its explicitly defined dependencies (using the requiredPlugins() attribute). It can then be put on hold until all these are loaded as well.
- To assist the previous point, deliver two artifacts: Your plugin as a whole, self contained with all related libraries, and a very small interface-jar that only contains your interfaces. This interface jar may be included by other plugin vendors using or implementing that interface. It is also safe to add the interface to an application's classpath.
|