6.2. Modules and PlugIns

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.

6.2.1. Differences between modules and plugins

[Note]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]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.

6.2.2. Modules

6.2.2.1. Module Architecture for the old implementation

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

    1. create the specified classes

    2. call initializeModule on this class

    3. place the class object into the internal list of modules

6.2.2.2. The ArgoModule interface - used in the old implementation

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.

6.2.2.3. Module Architecture for the new implementation

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.)

6.2.2.4. The ModuleInterface interface - in the new implementation

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.

6.2.2.5. Using Modules

When modules are used they can't be distinguished from the rest of the ArgoUML environment.

6.2.2.6. How do I ...?

  • ...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]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.

6.2.3. Plugins

[Note]Note

This description is for the old moduleloader.

6.2.3.1. Plugin Architecture

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.

6.2.3.2. How do I ...?

  • ...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]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?

    1. 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]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.

    2. 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"));
          }
      

6.2.4. Tip for creating new modules (from Florent de Lamotte)

[Note]Note

This description is for the old moduleloader.

Florent wrote a small tutorial for creating modules. It can be found on the ArgoPNO website.