HowTo JPF: Unterschied zwischen den Versionen

Aus Java Student User Group Austria - Java + JVM in Wien Österreich / Vienna Austria
Wechseln zu: Navigation, Suche
(introduction)
(create an extension)
Zeile 282: Zeile 282:
 
</code>
 
</code>
  
file <code>plugin.xml</code>
+
file <code>plugin.xml</code>:
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0" ?>
 
<?xml version="1.0" ?>
Zeile 302: Zeile 302:
 
</source>
 
</source>
  
 +
file <code>ExportMenuBarPlugin.java</code>:
 
<source lang="java">
 
<source lang="java">
 
package phudy.jpf.pluginexport;
 
package phudy.jpf.pluginexport;
Zeile 308: Zeile 309:
  
 
public class ExportMenuBarPlugin implements IMenuBarPlugin {
 
public class ExportMenuBarPlugin implements IMenuBarPlugin {
 
 
public String getTitle() {
 
public String getTitle() {
 
return "Export";
 
return "Export";
}
 
 
public int getWeight() {
 
return 500;
 
 
}
 
}
  
Zeile 320: Zeile 316:
 
System.out.println("exec export");
 
System.out.println("exec export");
 
}
 
}
 
 
}
 
}
 
 
</source>
 
</source>
  

Version vom 30. Dezember 2008, 21:56 Uhr

written by Christoph P; created Tuesday, December 30, 2008

introduction

"JPF provides a runtime engine that dynamically discovers and loads "plug-ins". A plug-in is a structured component that describes itself to JPF using a "manifest". JPF maintains a registry of available plug-ins and the functions they provide (via extension points and extensions).

One major goal of JPF is that the application (and its end-user) should not pay any memory or performance penalty for plug-ins that are installed, but not used. Plug-ins are added to the registry at application start-up or while the application is running but they are not loaded until they are called." http://jpf.sourceforge.net/

some nice characteristics/features it provides:

  • open source (LGPL)
  • low barrier of entrance
  • ant tasks
  • hot deployment
  • lazy loading
  • integrity check
  • embedded documentation

architecture overview

in the following example i will show you a simple demo application, divided in three main parts: the boot module (got the main method and fires up jpf), core module (alias application plugin, will display the main window and provide extension points for menubar entries) and the export module (imports core and uses the extension point to add a single entry to the menubar).

+--------------------------------------------+
|                                            |
|                  boot                      |
|                                            |
+--------------------------------------------+
|              |              |              |
|     core     |    export    |      ...     |   
|  (plugin_0)  |  (plugin_1)  |  (plugin_n)  |
|              |              |              |
++++++++++++++++++++ JPF +++++++++++++++++++++
************ Java, VM, Classloader ***********

as you can see the core plugin (extends from ApplicationPlugin) itself is a plugin.

the plugin concept

the three main parts are decalred in the so-called manifest file (plugin.xml):

  • plugin: with jpf the whole application is separated in plugins (kind of module). it holds the code, resources, libraries and defines requirements/imports, exports, extension-points and extensions.

<!ELEMENT plugin (doc?, attributes?, requires?, runtime?,
                  (extension-point|extension)*)>
<!ATTLIST plugin id         CDATA #REQUIRED>
<!ATTLIST plugin version    CDATA #REQUIRED>
<!ATTLIST plugin vendor     CDATA #IMPLIED>
<!ATTLIST plugin class      CDATA #IMPLIED>
<!ATTLIST plugin docs-path  CDATA #IMPLIED>

  • extension-point: an interface which is provided by the plugin. an extension-point is identified by its plugin-id and extension-point-id, and defines parameters for that interface.

<!ELEMENT extension-point (doc?, parameter-def*)>
<!ATTLIST extension-point id                CDATA #REQUIRED>
<!ATTLIST extension-point parent-plugin-id  CDATA #IMPLIED>
<!ATTLIST extension-point parent-point-id   CDATA #IMPLIED>
<!ATTLIST extension-point extension-multiplicity  (any | one | one-per-plugin
                                                  | none) "any">

<!ELEMENT parameter-def (doc?, parameter-def*)>
<!ATTLIST parameter-def id             CDATA #REQUIRED>
<!ATTLIST parameter-def multiplicity   (one | any | none-or-one | one-or-more)
                                       "one">
<!ATTLIST parameter-def type           (string | boolean | number | date | time
                                       | date-time | null | any | plugin-id
									   | extension-point-id | extension-id
									   | fixed | resource) "string">
<!ATTLIST parameter-def custom-data    CDATA #IMPLIED>
<!ATTLIST parameter-def default-value  CDATA #IMPLIED>

  • extension: could be thought as an implementation for a given interface. a plugin can extend the extension-point of another plugin and pass concrete values as arguments (most of the time only a "class" attribute which holds the full qualified name of a class).

<!ELEMENT extension (doc?, parameter*)>
<!ATTLIST extension plugin-id  CDATA #REQUIRED>
<!ATTLIST extension point-id   CDATA #REQUIRED>
<!ATTLIST extension id         CDATA #REQUIRED>
<!ATTLIST extension optional   (true | false) "false">

<!ELEMENT parameter (doc?, value?, parameter*)>
<!ATTLIST parameter id     CDATA #REQUIRED>
<!ATTLIST parameter value  CDATA #IMPLIED>

you can of course have a look at the full DTD hosted on the jpf website.

example

startuping up the framework

you can start the java plugin framework either by simply providing a boot.properties file and executing a utility jar, or by writing custom code which gives you full control of boot procedure (preferred way). the last option is nevertheless required for unit tests.

let properties-file do the work (simple)

file boot.properties

# application plugin id
org.java.plugin.boot.applicationPlugin = at.ac.tuwien.jsug.jpf.startup
# [ full | light | off ]
org.java.plugin.boot.integrityCheckMode = light
#org.java.plugin.boot.splashImage = ${applicationRoot}/splash.png

#-------------------------------------------------------------------------------
# JPF runtime configuration

org.java.plugin.PathResolver = org.java.plugin.standard.ShadingPathResolver
org.java.plugin.standard.ShadingPathResolver.shadowFolder = ${applicationRoot}/temp/.jpf-shadow
org.java.plugin.standard.ShadingPathResolver.unpackMode = smart
org.java.plugin.standard.ShadingPathResolver.excludes = CVS

#-------------------------------------------------------------------------------
# could be some more properties defined for own usage

execute following line on the command line interface: java -jar lib/jpf-boot.jar

take over full control (advanced)

<source lang="java"> package at.ac.tuwien.jsug.jpf.boot;

import javax.swing.SwingUtilities; import org.java.plugin.ObjectFactory; import org.java.plugin.PluginManager; import org.java.plugin.PluginManager.PluginLocation; import org.java.plugin.boot.DefaultPluginsCollector; import org.java.plugin.util.ExtendedProperties; import at.ac.tuwien.jsug.jpf.core.ICoreApplicationPlugin;

/**

* entry point for the application to bootstrap jpf and
* invoke CoreApplicationPlugin.startApplication().
*/

public class JpfBooter { private static final String PLUGINS_REPOSITORY = "./plugins";

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { new App().start(); }}); }

private void start() { final PluginManager manager = ObjectFactory.newInstance().createManager(); final DefaultPluginsCollector collector = new DefaultPluginsCollector(); final ExtendedProperties props = new ExtendedProperties(); props.setProperty("org.java.plugin.boot.pluginsRepositories", PLUGINS_REPOSITORY);

try { collector.configure(props); manager.publishPlugins(collector.collectPluginLocations().toArray(new PluginLocation[] {}));

final ICoreApplicationPlugin corePlugin = (ICoreApplicationPlugin) manager.getPlugin("at.ac.tuwien.jsug.jpf.core"); corePlugin.startApplication(); } catch (Exception e) { e.printStackTrace(); } } } </source>

create the main boot part

* src/main/java/
  - at.ac.tuwien.jsug.jpf.boot.JpfBooter.java
* src/main/resources/
  - log4j.properties
* plugins/
  - at.ac.tuwien.jsug.jpf.core/ ... target output for core plugin
  - at.ac.tuwien.jsug.jpf.export/ ... target output for export plugin

create the core application plugin

* src/main/java/
  - at.ac.tuwien.jsug.jpf.core.CoreApplicationPlugin.java
  - at.ac.tuwien.jsug.jpf.core.ICoreApplicationPlugin.java
  - at.ac.tuwien.jsug.jpf.core.IMenuBarPlugin.java
* src/main/resources/
  - plugin.xml

file plugin.xml <source lang="xml"> <?xml version="1.0" ?> <!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 1.0" "http://jpf.sourceforge.net/plugin_1_0.dtd"> <plugin id="at.ac.tuwien.jsug.jpf.core" version="0.0.1" class="at.ac.tuwien.jsug.jpf.core.CoreApplicationPlugin">

<runtime> <library id="core" path="/" type="code"> <export prefix="*" /> </library> </runtime>

<parameter-def id="class" /> </extension-point> </plugin> </source>

you might want to use the following handy method for retrieving plugins: <source lang="java"> @SuppressWarnings("unchecked") public static <T> List<T> fetchPlugins( final Plugin plugin, final String extPointPluginId, final String extPointId, final String attributeName) throws Exception { final List<T> result = new LinkedList<T>();

final PluginManager manager = plugin.getManager();

final ExtensionPoint extPoint = manager.getRegistry().getExtensionPoint(extPointPluginId, extPointId); for (final Extension extension : extPoint.getConnectedExtensions()) { // LOG.info("Processing extension point: " + extension);

final PluginDescriptor extensionDescriptor = extension.getDeclaringPluginDescriptor(); manager.activatePlugin(extensionDescriptor.getId()); final ClassLoader classLoader = manager.getPluginClassLoader(extensionDescriptor); final String pluginClassName = extension.getParameter(attributeName).valueAsString(); final Class<T> pluginClass = (Class<T>) classLoader.loadClass(pluginClassName); final T pluginInstance = pluginClass.newInstance(); result.add(pluginInstance); }

return result; } </source>

create an extension

* src/main/java/
  - at.ac.tuwien.jsug.jpf.export.ExportMenuBarPlugin.java
* src/main/resources/
  - plugin.xml

file plugin.xml: <source lang="xml"> <?xml version="1.0" ?> <!DOCTYPE plugin PUBLIC "-//JPF//Java Plug-in Manifest 1.0" "http://jpf.sourceforge.net/plugin_1_0.dtd"> <plugin id="at.ac.tuwien.jsug.jpf.export" version="0.0.1">

<requires> <import plugin-id="at.ac.tuwien.jsug.jpf.core" /> </requires>

<runtime> <library id="src" path="/" type="code" /> </runtime>

<extension plugin-id="at.ac.tuwien.jsug.jpf.core" point-id="MenuBar" id="ExportMenuBar"> <parameter id="class" value="at.ac.tuwien.jsug.jpf.export. ExportMenuBarPlugin" /> </extension> </plugin> </source>

file ExportMenuBarPlugin.java: <source lang="java"> package phudy.jpf.pluginexport;

import at.ac.tuwien.jsug.jpf.core.IMenuBarPlugin;

public class ExportMenuBarPlugin implements IMenuBarPlugin { public String getTitle() { return "Export"; }

public void execute() { System.out.println("exec export"); } } </source>

appendix

links

notes

  • although it is more common to write "plug-in" instead of "plugin", i am going not to write the additional "-" character :)