The ArgoUML tool provides a basis for UML design and potentially an executable architecture environment for more specialized applications. This is solved by a clear interfaces between the ArgoUML core and the extensions. Extensions are called modules.
![]() | Note |
---|---|
This description is only relevant for the old moduleloader since the plugins concept is not used in the new one. |
In the old moduleloader implementation the classes within the modules that attach to ArgoUML core are called plugins. In the new moduleloader implementation they don't have any special name.
Modules
A module is a collection of classes and resource files that can be enabled and disabled in ArgoUML. Currently this is decided by the modules' availability when ArgoUML starts but in the future it could be made possible to enable modules from within a running ArgoUML.
This module system is the extension capability to the ArgoUML tool. It will give developers of ArgoUML and developers of applications running within the ArgoUML architecture the ability to add additional functionality to the ArgoUML environment without modifying the basic ArgoUML tool. This flexibility should encourage additional open source and/or commercial involvement with the open source UML tool.
The module extensions will load when ArgoUML starts. When the modules are loaded they have the capability of attaching to internal ArgoUML architectural elements. Once the plugins are attached, the plugins will receive calls at the right moment and can perform the correct action at that point.
Modules can be internal and external.
The only difference is that the internal modules are part of
the argouml.jar
and the external are
delivered as separate jar-files.
Plugins
![]() | Note |
---|---|
This description is for the old moduleloader. |
A plug-in in ArgoUML is a module
that implements the
org.argouml.application.api.Pluggable
interface.
The Pluggable
interface
acts as a passive dynamic component,
i.e. it provides methods to simplify the attaching
of calls at the correct places.
There are several Pluggable
interfaces
that each simplify the addition of one kind of object.
Examples PluggableMenu
,
PluggableNotation
.
One Module can implement several Pluggable
interfaces.
This is essentially and implementation of the Dynamic Linkage pattern
as described in
Patterns in Java Volume 1 by Mark Grand ISBN 0-471-25839-3.
The whole of ArgoUML Core is the Environment,
the classes inheriting Pluggable
are the
AbstractLoadableClass.
The controlling class of the module/plugin extension
is org.argouml.application.modules.ModuleLoader
.
ModuleLoader
is a singleton
created in the ArgoUML main initialization routine.
ModuleLoader
will:
read in the property file
for each of the classes found
create the specified classes
call initializeModule
on this class
place the class object into the internal list of modules
Each class must derive from the ArgoModule
interface.
This interface provides the following methods:
String getModuleName
(
void)
;
String getModuleDescription
(
void)
;
String getModuleVersion
(
void)
;
String getModuleAuthor
(
void)
;
provides information about the ArgoUML module.
boolean initializeModule
(
void)
;
initializeModule
is called
when the class loader has created the module,
and before it is added into the modules list.
initializeModule
should
initialize any required data
and/or attach itself as a listener to ArgoUML actions.
initializeModule
for all modules is invoked
after the rest of ArgoUML has been initialized and loaded.
Any menu modifications or system level resources
should already be available
when the module initialization process is called.
initializeModule
should return true
if the initialization is successful
(or if no initialization is necessary).
The only available mechanism for handling dependencies between modules is the order in which they are read from the file.
void shutdownModule
(
void)
;
The shutdownModule
method is called
when the module is removed.
It provides each module the capability
to clean up or save any required information
before being cleared from memory.
void setModuleEnabled
(
boolean tf)
;
boolean isModuleEnabled
(
void)
;
Reserved for future implementation.
Vector getModulePopUpActions
(
void)
;
Reserved for future implementation.
The plan is to have this called for each module when the module should add its entries in PopUpActions.
String getModuleKey
(
void)
;
Returns a string that identifies the module.
The controlling class for the new implementation is
org.argouml.moduleloader.ModuleLoader2
.
It is a singleton created when first used.
It is first used in the main initialization routine.
When created it searches through all available modules and creates
a list of their main objects
(implementing ModuleInterface
).
Currently (September 2004) this also means that the found modules are
by default selected i.e. they are marked to be enabled.
At the end of the main initialization routine the selected modules are enabled. (The original idea was to do this several times during the main routine to allow for modules to add command line arguments, add languages, and make functions available for batch command, but the example used for testing loaded the ProjectBrowser "too early" and the result wasn't so good. I (Linus) hopes this can be eventually fixed.)
Each class used by the ModuleLoader2
must implement the ModuleInterface
interface.
This interface has methods for enabling, disabling and identifying the module.
When a module is enabled it is expected to register some class wherever it affects ArgoUML using the interfaces provided there. Since the same interfaces and registration mechanism is used internally within ArgoUML there is a small likelyhood that there already is an interface and a possibility to register. If there isn't, ArgoUML cannot currently be extended at that point. If you still need ArgoUML to be extended at that point you will have to work in getting this interface or registration mechanism implemented within ArgoUML. (This could also be another module that has to be amended.)
Classes administered by the module that registers to whatever place of ArgoUML they are interested in, does not need to have any connection to the module loader. They are written exactly as if they would have been if they were part of the core ArgoUML.
When modules are used they can't be distinguished from the rest of the ArgoUML environment.
...tell when a module is enabled?
The method isEnabled
in ModuleLoader2
returns true if the module with that name is enabled and false otherwise.
![]() | Note |
---|---|
This only works for modules enabled in the new module loader. For modules loaded using the old module loader, it is not possible to determine if they are enabled. |
![]() | Note |
---|---|
This description is for the old moduleloader. |
Each class must derive from the Pluggable
interface.
In addition to the methods declared in ArgoModule
,
which Pluggable
extends
(see Section 6.2.2.2, “The ArgoModule interface - used in the old implementation”),
the interface provides the following method:
boolean inContext
(
Object[] context)
;
inContext
allows a plug-in to decide
if it is available under a specific context.
One example of a plugin with multiple criteria is the PluggableMenu. PluggableMenu requires the first context to be a JMenuItem which wants the PluggableMenu attached to as the context, so that it can determine that it would attach to a menu. The second context is an internal (non-localized) description of the menu such as "File" or "View" so that the plugin can further decide.
...create a pluggable settings tab?
...
...create a pluggable menu item?
Look at the modules junit and menutest for examples of how to add to menus using the PluggableMenu interface.
The implementation of inContext() that you provide should be similar to:
public boolean inContext(Object[] o) { if (o.length < 2) return false; if ((o[0] instanceof JMenuItem) && ("Create Diagrams".equals(o[1]))) { return true; } return false; }
The string "Create Diagrams" is a non-localized key string passed in ProjectLoader at about line 440 in the statement
appendPluggableMenus(_createDiagrams, "Create Diagrams");
There is no restriction on a single class implementing multiple plugins - quite the contrary, that is one of the reasons for providing the generic Pluggable interface that PluggableThings extend.
...create a pluggable notation?
...
...create a pluggable diagram?
Let's say we want to enable a new diagram type as a plug-in. We use the interface PluggableDiagram that uses a method that returns an JMenuItem object:
public JMenuItem getDiagramMenuItem();
The returned menu item will be added to the diagrams menu to allow to open a new diagram of this type.
In this example we do this by creating a helper class in the package org.argouml.application.helpers that implements the created plug-in interface PluggableDiagram, and call it DiagramHelper:
public abstract class DiagramHelper extends ArgoDiagram implements PluggableDiagram { /** Default localization key for diagrams */ public final static String DIAGRAM_BUNDLE = "DiagramType"; /** String naming the resource bundle to use for localization. */ protected String _bundle = ""; public DiagramHelper() { _bundle = getDiagramResourceBundleKey(); } public void setModuleEnabled(boolean v) { } public boolean initializeModule() { return true; } public boolean inContext(Object[] o) { return true; } public boolean isModuleEnabled() { return true; } public Vector getModulePopUpActions(Vector v, Object o) { return null; } public boolean shutdownModule() { return true; } public JMenuItem getDiagramMenuItem() { return new JMenuItem(Argo.localize(_bundle,"diagram_type")); } public String getDiagramResourceBundleKey() { return DIAGRAM_BUNDLE; } }
The extension of ArgoDiagram is specific to this example; the plug-in will provide a new ArgoUML diagram.
![]() | Important |
---|---|
Don't forget to do the localization stuff, because the plug-in might be used in all languages ArgoUML offers! |
...do the localization stuff (not plug-in specific, but important)?
...
...create a pluggable resource bundle?
...
...create a new pluggable type?
Create the plug-ins interface
In the package org.argouml.application.api, create an
interface that extends Pluggable
(in the same package).
The class name must begin with 'Pluggable'.
![]() | Note |
---|---|
One of the main purposes of a plugin is to provide the capability to add an externally defined class that will be used by ArgoUML in the same way as a similar internal class. This means that modifications are needed all over ArgoUML in order to call the pluggable interface. Therefore this must be done in ArgoUML itself and cannot be done in any module. |
It now inherits from ArgoModule
the methods
public boolean initializeModule(); public boolean shutdownModule(); public void setModuleEnabled(boolean tf); public boolean isModuleEnabled(); public String getModuleName(); public String getModuleDescription(); public String getModuleVersion(); public String getModuleAuthor(); public Vector getModulePopUpActions(Vector popUpActions, Object context); public String getModuleKey();
and from Pluggable
the methods
public boolean inContext(Object[] context);
and thus provides the basic mechanism that plug-ins need.
Decide in what context this is to be enabled and add calls there
It is useful for those plugins which actually use context to provide a helper method
Object[] buildContext
( | classtype1 | parameter1, |
classtype2 | parameter2) ; |
which will serve two purposes.
First, it will provide a simple way of creating the Object[] parameter.
Second, it helps to document the context parameters within the class itself.
Again using PluggableMenu
as an example,
it contains the function
public Object[] buildContext(JMenuItem parentMenuItem, String menuType);
which is used as follows:
if (module.inContext(module.buildContext(_help, "Help"))) { _help.add(module.getMenuItem(_help, "Help")); }
![]() | Note |
---|---|
This description is for the old moduleloader. |
Florent wrote a small tutorial for creating modules. It can be found on the ArgoPNO website.