Quickstart

There is a blank plugin that can be used as a basis for you own plugins. You can download this blank plugin using CVS, it's part of the Plugins cvs module. Once you get that blank plugin, you will have to modify a few files:

  • pom.xml This file should contain information which is corresponding to your plugin (author, name, description, version, remove inheritance to plugins-base). This is a maven2 project descriptor. Don't forget to add a <dependency> to the JPEd version you want to work on. Example:
      <dependencies>
        <dependency>
          <groupId>net.sf.jped</groupId>
          <artifactId>jped</artifactId>
          <version>2.0</version>
        </dependency>
      </dependencies>
  • META-INF/services/org.jped.plugins.PluginsProvider This file should contain a single line, representing the classname of your implementation of the PluginsProvider

    Once the files have been modified, create your implementation of PluginsProvider . This interface simply provides a getter that returns a Plugin array. Each element of this Array is a Plugin. Look at the Plugin javadoc to see what you can do from plugins. Or read below. Once the your plugin is ready and compiles, use maven to create the package (mvn package ) and store it in the plugins/ folder of JPEd.

Plugin lifecycle

JPEd scans the plugins folder and the java classpath for jars providing the PluginsProvider service. It then request from each of this service implementation for a list of Plugin. So it's up to the PluginsProvider to instanciate the plugins. Once JPEd has collected Plugin instances, it starts initialising it. It first calls on each Plugin the getInfo() method to find out plugin name, description and version. It then call the init() metod of the plugin. Depending on the return value, the plugin might be discarded. Once all plugins have been initialised, JPEd go on with it's own initialisation stuff. After full JPEd initialisation, the postInit() method of each plugin is called. This give the opportunity to plugins to access menus and object tree of JPEd and, eventually, to alter it (add menu items, remove some toolbar, aso).

Wizards

Wizards are a very userfriendly way to create workflow files. If you are a company using workflow, you might want that all your workflow packages have a specific configuration, like some general imports, a default script language, a list of available workflow applications, etc. Each plugin can provide several wizards. If you don't provide any wizard, please return an empty array to the getWizards() method of your plugin. The name, icon and description of wizard is used to display, to the user, a list of wizard to choose from when creating a new package. It will show a list of icon, with the name at the bottom. During a rollover, the description area of the UI will show the description of that wizard. The description can be in html, for prettier display.

Once a user has selected a wizard, JPEd will hide the wizards dialog and call the run method of selected wizard. From that point, it's up to the wizard to use the JPEd api to create proper XPDL. It may show additional dialogs and so on, even decide not to create a new file in the end. For example of a wizard, see SimpleNewPackageWizard . It's just a basic wizard that create a simple XPDL without any pre-initialisation.

PDFPlugin

This kind of plugin allows you to alter, or completly change, the behaviour of PDF generation. To do this, plugins are allowed to work at 3 levels:

  1. The document level, allows you to create a full PDF document. At this level you have the possibility to create your own section and pilot the whole document layout.
  2. The Section level, allows you to alter PDF sections of the PDF document. This is a finer level. At this level, you can, for example, add additionnal information to an existing section, or completly remove an existing section.
  3. The element level, allows you to alter fine parts of PDF document. You can, at this level, decide to hide some extended attributes from report, decide that a Transition should be drawn allow with a visual picture, decide that the description of an activity should include a picture, and so on.

    Whatever level you are on, the process is always the same, you are always responsible to process or not the chain for a given element. To prevent an element from appearing in the resulting pdf document, ensure your plugin has higher priority, and bypass the chain. Each chain has a next() method that return an objet of appropriate type. To bypass, just don't call it. To alter, call it and do whatever you want with result. To just don't do anything special, call next() and return it's result to caller.

The "chain"

The PDF generation is organized in a chain. The plugins manager construct, for each element to render, a chain of PDF plugin, using the priority of plugins. The chain contains various important information:

  • The Task element, which is used to visually track the progress. It display informations to user ("generating Activity list","generating graph",...) and a progress bar. See below for details on how to properly track task informations
  • The XMLElement under process at the moment of the call. It can be anything rangin from a WorkflowProcess to an ExtendedAttribute. Up to the plugin to decided what to do depending on type of element (instance of is your friend).
  • The enclosing section. The PDF parent section. This is pretty usefull if you create new paragraph / section / tables / whatever, because you might need a way to attach it to parent PDF element or will need to call parent element to create subelements!
  • next(), which executes the chain forward and return the result.

    There are basically 3 mode of operation, depending on what you want your plugin to do for a specific element:

    1. Dropping element from pdf. This is the simplest thing to do. The element won't make it's way to the pdf, at least, not using the rest of the chain. Just don't call next() and don't do anything. Simply return null .
    2. Do it yourself. Similar to precedent, you won't call the chain. However, you will use the enclosing section information to return an iText element of the appropriate type.
    3. Pass it on. Yo don't do anything special with that element you don't handle? Then just issue a return chain.next() to ensure proper chain call.
    4. Add extra info. You will use both enclosing section and the result of next() to create more complex PDF output.

      In the last two case, a bit of reading of the iText documentation is very usfull :)

Document level

Most plugins won't do anything here, and will just return chain.next() . For details on how to handle the document level, take a look at the CorePdfPlugin

Section level

At this moment, the PDF generation process has asked you, explicitly to write a Section describing a specific XMLElement. That mean you must return an iText Section element (or null) that will get inserted in parent section or document by caller. Depending on conditions you might opt to create a new ChapterAutoNumber (top level section) or a new subsection inside of chain.getEnclosingSection() . However, care should be taken not to generat invalid PDF, and you should always check the status of enclosing section (existing or not) before deciding to generate a chapter or a simple subsection.

Here, you are free to make calls to chain.startProcessSection() to create various subsection informations to include in your section. In this case, the process is always as follow:

chain.getTask().start(2);
parent = chain.getEnclosingSection();
chain.setEnclosingSection(newSubSection);
chain.getTask().setCurrentStatus("processing someElement");
chain.startProcessSection(someElement, chain.getTask().subTask();
chain.getTask().setCurrentStatus("processing someOtherElement");
chain.startProcessSection(someOtherElement, chain.getTask().subTask();
chain.setEnclosingSection(parent); // restore state of chain before return

A good example of such behaviour can be found in CorePDFPlugin during generation of activity list.

Element level

The element level might be seen as the end part of the chaining process. It renders tiny part of text that get inserted in the various sections. This is the best place to hide some elements like extended attributes from user display. You will probably not generate subsection at this level, but you have access to enclosing section and may use it to insert complex table or just plain 5 words paragraphs :). You can also use the chain.startProcessElement() to gather related informations all around the XPDL. For example, a Transition might show the name of activity end points it relates to, a workflow variable might point out at which level it go defined (importation? package level? workflow level?)

Task management

A task is divided in steps. A task also has a currentStatus that is a visual text describing what you are currently doing. To end a task, you must finish all it's steps. Steps are arbitrary division of the work. To finish a step, there are two ways. Either call task.nextStep(), this finishes current step, either create a subtask for current step and finish that subtask. Here is an example of a complete task lifecycle.

// task= ... 
task.start(4);     // a task of 4 steps
task.nextStep();   // first step finished
subtask = task.subTask();  // create a sub task
subtask.start(2);          // subtask has 2 steps
subtask.nextStep();        // First step of subtask finished
subtask.nextStep();        // Finish subtask work
                           // Now main task has 2 step out of 4 finished
subtask = task.subTask();  // create another sub task
subtask.start(1);          // subtask has only 1 steps
subtask.nextStep();        // Finish subtask work
                           // Now main task has 3 step out of 4 finished
task.nextStep();           // main task has finished.
                           // Note: at each level of code, you are free to call task.setCurrentStatus(), as 
                           // it's just informational text

At each step of PDF generation, plugins are asked to do task management *if* they do some work. On each chain, there is a TaskProgress accessible using chain.getTask() and that is usefull to track generation work. A task is divided in steps, as described above. When you are starting some work that you think might justify task informations, you have to follow this decision process. * If chain's task is already started (hasStarted()=true), and you think you need X steps, issue a task.addSteps(X) . If the task has not yet started, issue a task.start(X) . If you make calls to chain.startProcessXXXX methods, it's recommended you add a step for each call and pass to the calls a task.subTask() parameter. As such, the call is seen as a subtask executing in current step.

Altering display values of elements

JPEd allows you to visually alter the String representation of any XMLElement. It is used almost everywhere where XMLElement.getValue() is needed to do a visual readonly representation of this element. Examples include tooltips and representation in PDF. It use a very simple plugin, DisplayNameGeneratorPlugin that does not need lots of description. It has a single method, getDisplayName() that returns a String. If the return value is null, it's assumed this Plugin does not alter element given as argument. Otherwise, return value is taken as visual representation of that element.

ValueFilter

This interface will be removed in future version of JPEd and should not be used. It was aimed to do same work as DisplayNameGenerator, but in a more complex way. It currently does nothing.

Manipulating tooltips

JPEd generates tooltips of all XMLElement in a centralized way, which is pluggable using the TooltipGeneratorPlugin . This is also a simple plugin, with only one method getPluggedTooltip that will alter a tooltip for a given element. All tooltips in JPEd are html based. The plugins must output a correct html as understood by the java sun implementation (see swing javadoc), with the exception that <html> and </html> must not be present. If you don't want to alter tooltip for a specific element, just return the value passed as argument. You can append to an existing tooltip or completly remove it, depending on your mood.

Values editors

JPEd provide a mean to completly change the editors associated to any XMLElement using the PanelGeneratorPlugin . There are 3 levels at which you can interact with the process of creating visual element editors.

  • Alter an existing XMLPanel using the getPuggedComponent method
  • Alter a property editor, using the getPluggedValueComponent
  • Update the set of hidden elements (element that won't get displayed, even if some pluggin has custom component for it) using the updateHiddenElements(String, XMLCollection, Set) and updateHiddenElements(String, XMLComplexElement, Set)

    The 2 updateHiddenElements methods are easy to understand. You get an XMLElement and a list of already hidden elements you may change. You may add or remove elements from the set. The set contains XMLElement items.

    the two getPluggedXXXX method need a bit more clarification. They receive as parameter an XMLPanel that represent the current state of that element editor. They are allow to alter it in several way

  • Completly replace it with another implementation.
  • Prepend an XMLPanel to the existing one.
  • Append an XMLPanel to the existing one.
  • Put the XMLPanel in a tabbedpane and add a new tab to it.

    Those possibilities are exclusive. You can't prepend and append. One way to do this would be to replace and using in your own implementation the XMLPanel given as call argument. This various possibilities is the whole purpose of the retun value, which is a wrapper around an XMLPanel providing position information and replacement informations. It's recommended, for easyness, that you use the various constructor of BasicPluggedComponent to create your return value.

    For examples of using the PanelGeneratorPlugin, it's recommended to take a look at the source code of existing plugins.

Storage manipulation

It's possible to alter, during the load process, the content of XPDL packages. This is done by using the StoragePlugin . Returning ADJUST_RELOAD from the call forces JPEd to reread the file from the filesystem