Developing CAM plugins
This description assumes that you have knowledge of the Java programming language and are comfortable writing and compiling Java programs. If not, The Java tutorials are a helpful introduction to the language.
Basic steps in writing a plugin (module)
CAM's modular structure means that it is very easy to add functionality by writing "plugins" in Java. Each plugin "plugs into" a "plugin point" defined by another module in CAM, and can define its own plugin points for future expansion. Each plugin point has a name and an associated abstract Java class, which defines the API for the plugin point. The default distribution of CAM provides many plugin points, for different functions - see the next section for a selection.
The basic steps in writing a plugin are:
- Identify an appropriate plugin point for the plugin's functionality (see below);
- Write a Java class that extends the class associated with the plugin point;
- Compile all the relevant classes into a JAR file;
- Put an appropriate modules.xml file into the root directory of the JAR file (see below);
- Copy the JAR file into the "modules" subdirectory of the CAM directory (if you have set up the build environment as described on this page, this step should happen automatically);
- Restart CAM - the new module should appear! (If not, check the command prompt window for error messages.)
Additionally, if your project relies on any external libraries, you will need to give each of their JAR files an appropriate modules.xml file and copy them into the CAM modules directory too.
The individual steps are detailed below.
Plugin points
Every CAM module must extend a plug-in point. Depending on the plug-in point which your code extends, it will be triggered at a different point in the CAM interface. For instance, your module may provide a new button on the main CAM toolbar, or it may provide a new type of chart which appears in the list of charts that can be created when visualising simulation output.
This is a list of commonly-used plugin points in the form module-id - plugin-point-id - plugin-point-class: description
- default-application - application-menu-plugin - p3.dflt.application.P3DefaultApplicationMenuPlugin: Something that can be triggered by clicking on its entry that appears on the "Plugins" menu
- asm-core - asm-application-menu-plugin - asm.application.ASMApplicationMenuPlugin: Something that can be triggered by clicking on a button in the toolbar when looking at a specific worksheet, and which can specify the type of palette model that it understands
- simulation-results-explorer-plugin - long-value - value.LongValuePlugin: A ValueProvider that returns a long (integer) numerical value from a data point
- simulation-results-explorer-plugin - double-value - value.DoubleValuePlugin: A ValueProvider that returns a double (floating-point) numerical value from a data point
- simulation-results-explorer-plugin - category-list - categorylist.CategoryListPlugin: A ValueProvider that returns a list of strings corresponding to the "categories" that a data point is a member of
- p3-core - command-line-plugin - p3.application.P3CommandLinePlugin: Something that can be called on the command line
- p3-core - property - p3.model.P3Property: A new type of P3Property
- simulation-results-explorer-plugin - view - view.plugin.SimulationResultsViewPlugin: A new type of chart for data analysis
Writing a modules.xml file
The modules.xml file is needed to tell CAM how each plugin will be linked with the rest of the code.
The best way to add modules.xml into a module's JAR file is to put it in the "default package" location within your IDE (even though it's not a Java file!). Alternatively, since a JAR file is just a ZIP file, once it has been compiled: rename it from xyz.jar to xyz.zip; open the zip file; copy in the modules.xml file; and rename it back to xyz.jar. You will probably need this method if you are using external libraries for which you do not have the original source code.
The format of modules.xml depends on whether the JAR file you are inserting it into is a plugin (i.e. plugs into a specific plugin point) or a library (referenced by a plugin). Templates for both formats are shown below.
N.B. Since modules.xml is an XML file, there are certain characters you cannot use in the "free text" parts, in particular &,<, >, ' and ". To use these, you have to replace each one with the corresponding "escape sequence":
- &: &
- >: >
- <: <
- ': '
- ": "
(the colon and the space aren't part of the escape sequence but the semicolon is, so to write e.g. A&B you'd write A&B in the appropriate part of the file)
Format of modules.xml required to accompany a module
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file details each module the package contains, together with any dependencies upon other modules. It should go in the root directory of a package's JAR file. -->
<p3-modules>
<p3-module class="[the fully-qualified name of the class that implements the plugin point]">
<uid>[some form of unique identifier for the plugin, all lower case with dashes "-" to separate words]</uid>
<description>[A description of the plugin, free text]</description>
<authors>[Authors, free text]</authors>
<license>[Any license or copyright statement associated with the plugin]</license>
<plugin-point module="[the UID of the module this plugin plugs into]" id="[the name of the plugin point]"/>
<implements version="[the version number of this plugin, up to 3 digits separated by full stops e.g. 0.0.1]"/>
<!--Any modules or libraries the plugin depends on, apart from the ones it plugs into-->
<requires uid="[module UID]" version="[minimum module version]"/>
<!--The plugin points the module itself defines, if any-->
<defines-plugin-point class="[The fully-qualified name of an abstract class that defines the plugin's API]">
<id>[a UID for the plugin point]</id>
<description>[A description of the plugin point]</description>
<required>[Whether there must be a module that implements this plugin point in order for *this* module to run, "true" or "false"]</required>
</defines-plugin-point>
</p3-module>
<!--Add other module definitions here if required-->
</p3-modules>
Format of modules.xml to accompany a library
If your module requires a java library (ie. a .jar file, which provides certain functionality and which you need to include in the java classpath to compile your module) you need to do the following things to tell CAM to link the library correctly to your module:
- Add a modules.xml to the library .jar file, by unpacking the jar as explained above and adding an xml file to the root folder of that jar.The XML should have the syntax explained below.
- Copy the modified library .jar file to the CAM modules directory.
- Ensure that an appropriate <requires> element is specified in the modules.xml of the module which requires the library; ensure that it gives the correct UID and version for the library. This tells CAM that the module depends on the library, and ensures that the CAM classloader loads the library first and makes its content visible to code in the module.
If you do not follow these steps correctly, you will see a ClassNotFoundException in the console output when you start CAM, and your module will not be loaded.
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file details the library this package contains, together with any dependencies upon other modules/libraries. It should go in the root directory of a library's JAR file. -->
<library>
<uid>[some form of unique identifier for the library, all lower case with dashes "-" to separate words]</uid>
<description>[A description of the plugin, free text]</description>
<authors>[Authors, free text]</authors>
<license>[Any license or copyright statement associated with the library]</license>
<implements version="[the version number of this library, up to 3 digits separated by full stops e.g. 0.0.1]"/>
<!--Any modules or libraries the library depends on-->
<requires uid="[module UID]" version="[minimum version]"/>
</library>