public class PluginManager extends Object implements FilenameFilter
Plugin manager provides framework for installation, uninstallation and loading of plugins and serves as a central point of plugin instantiation for all plugin factories.
This class implements the singleton pattern. As it has a private constructor,
direct instantiation is not possible and other classes must use the getInstance()
method to get a shared plugin manager instance.
The shared instance gets created when the getInstance()
method is called
for the first time. The manager first loads the default map of plugins
from the internal XML specified by the INTERNAL_PLUGIN_XML
variable.
If there's a custom plugin map found at the path specified by EXTERNAL_PLUGIN_XML
,
it is also loaded and applied to the default plugin map. Any changes caused during runtime
by plugin installation, uninstallation, enabling and disabling can be programatically saved
to the external XML through the savePluginMap()
method.
Plugins create hierarchy which is reflected in the plugin map structure. Each plugin must implement one of the public functional interfaces which were made loadable through the plugin mechanism. Such interfaces are called exposed functional interfaces or just functional interfaces.
Plugins implementing the same functional interface are said to belong to the same plugin group. There's an optional XML attribute allowing to assign name to such a group. As the name is displayed by the GUI, it should shortly describe functionality provided by the group (interface), for example "Desktop Clients", "Scripting Commands" etc.
Many objects in the T-Plan Robot Enterprise code base such as desktop clients, image comparison modules, script command handlers and report providers are already implemented as plugins. These are called default or built-in plugins and they are listed in the default plugin map PluginMap.xml. Plugins which are delivered in separate JAR/ZIP files or class paths are often called external plugins.
As built-in plugins form integral part of the product code, they cannot be uninstalled.
They can be however disabled and/or replaced (reimplemented) by external plugins.
To reimplement an internal plugin, a new one must be developed where its getCode()
method must return the same value as the internal one.
Each plugin must implement the Plugin
interface as well as one of
the exposed functional interfaces. Its class must be publicly instantiable which
means it can not be abstract or private. It also must have a default parameterless
constructor which initializes the instance correctly.
Plugins can be delivered in form of directories or JAR/ZIP files with compiled Java classes. Installation of plugins can be done in several ways:
getAvailablePlugins(java.io.File)
, installPlugin(java.lang.String, java.lang.String, boolean, boolean)
and
uninstallPlugin(com.tplan.robot.plugin.Plugin)
methods form more information.Format of the XML map is visible in the default PluginMap.xml. Meaning of individual tags/attributes are:
Tag/Attribute | Description |
"application" | Top level tag enclosing the T-Plan Robot Enterprise plugin configuration. |
"plugingroup" | This tag encloses group of plugins which implement the same functional interface. |
"interface" | Defines class of an exposed functional interface in Java format, for example "mypackage.MyFunctionalInterface". |
"name" | Specifies displayable name of a plugin group. It in fact serves as short description of functionality provided by the associated functional interface. |
"plugin" | This tag encloses a particular plugin. Its value must be plugin class in Java format, for example "mypackage.MyPlugin" External plugins are required to specify path to the library using the "source" attribute. |
"source" | Attribute of the "plugin" tag. It specifies path of the library (either a JAR/ZIP file or a single Java class path) which contains the compiled plugin class, for example "file:///usr/lib/myplugins.jar".
It is not mandatory for built-in plugins but other external plugins should always define it to make sure the classes are made visible to the JVM class loader. Failure to include the library path for an external plugin is likely to result in a ClassNotFoundException thrown at an attempt to install, enable or instantiate the plugin. |
"enabled" | Optional attribute of the "plugin" tag specifying whether the plugin is enabled or disabled. It's value may be either "true" or "false". It the attribute is not present, the attribute defaults to "true" meaning that the plugin is considered to be enabled. |
Modifier and Type | Field and Description |
---|---|
static String |
EXTERNAL_LEGACY_PLUGIN_XML |
static String |
EXTERNAL_PLUGIN_XML
Absolute path to external plugin map XML file.
|
static String |
INTERNAL_PLUGIN_XML
File name of the default plugin map XML.
|
static String |
XML_APPLICATION
Application XML tag ("application").
|
static String |
XML_ENABLED
Plugin status XML tag attribute ("enabled").
|
static String |
XML_GROUP_NAME
Plugin group name XML tag attribute ("name").
|
static String |
XML_GROUP_NAME_KEY
Resource bundle key of the plugin group name ("key").
|
static String |
XML_INTERFACE
Plugin group functional interface XML tag attribute ("interface").
|
static String |
XML_PLUGIN
Plugin XML tag ("plugin").
|
static String |
XML_PLUGINGROUP
Plugin group XML tag ("plugingroup").
|
static String |
XML_SOURCE
Plugin library path XML tag attribute ("source").
|
Modifier and Type | Method and Description |
---|---|
boolean |
accept(File dir,
String name) |
void |
addPluginListener(PluginListener listener)
Add a plugin listener.
|
static void |
addURL(URL u)
Add a URL dynamically to the class path.
|
static void |
discoverJavaTestScripts(File f)
Load Java test scripts contained in the argument file (class path, JAR or
ZIP file).
|
List<PluginInfo> |
getAvailablePlugins(File file)
Get plugins available in a JAR or ZIP file or Java class path.
|
static List<String> |
getInstalledPlugins()
Get the list of installed plugin unique IDs.
|
static PluginManager |
getInstance()
Get shared instance of Plugin Manager.
|
Map<Class,String> |
getInterfaceKeyMap()
Get map of exposed functional interfaces.
|
Map<Class,String> |
getInterfaceMap()
Get map of exposed functional interfaces.
|
Map<String,File> |
getJavaTestScriptMap()
Get the map of known Java test scripts.
|
static String |
getKnownGroupName(String group)
Get display name of a known plugin group.
|
static Map<String,String> |
getKnownGroups()
Get the map of known group IDs and resource bundle keys to their display
name.
|
static PluginDescriptor |
getPlugin(String uniqueId)
Get descriptor of an installed plugin.
|
List<PluginInfo> |
getPlugins()
Get list of all installed plugins which are present in the current plugin map.
|
List<PluginInfo> |
getPlugins(Class implementedInterface,
boolean includeDisabled)
Get list of plugin info instances for all plugins implementing a particular
functional interface.
|
List<PluginInfo> |
getPlugins(Object code,
Class implementedInterface,
boolean includeDisabled)
Get list of all installed plugins of the specific code and implemented interface
which are present in the current plugin map.
|
static List<String> |
getResources(Pattern pattern) |
static List<String> |
getResources(String element,
Pattern pattern) |
static List<String> |
getResourcesFromDirectory(File directory,
String directoryCanonicalPath,
Pattern pattern) |
static Collection<String> |
getResourcesFromJarFile(File file,
Pattern pattern)
List resources from a JAR or ZIP file.
|
static boolean |
implementsInterface(Class cl,
Class interf)
Find out whether a class implements a particular interface.
|
void |
installPlugin(String pluginClassName,
String libraryUrl,
boolean force,
boolean enable)
Install and eventually enable an external plugin.The method adds the plugin to the
internal plugin map and if the
enable flag is set to true, it
makes it enabled and thus immediatelly visible to all plugin
factories.If the plugin library is not present on the Java class path,
it is dynamically added and all its classes are made available to the default
JVM class loader.Successful installation of a plugin fires a plugin event
with the PluginEvent.PLUGIN_INSTALLED code to all registered listeners. |
boolean |
isBuiltIn(Plugin plugin)
Find out whether a plugin is a default (built-in) one.
|
boolean |
isEnabled(Plugin p)
Find out whether a plugin is disabled or enabled.
|
boolean |
isInstalled(Plugin p)
Find out whether a plugin is already installed.
|
static boolean |
isPlugin(File archive)
Test out if the argument ZIP archive is a plugin.
|
void |
removePluginListener(PluginListener listener)
Remove an object from the list of plugin listeners.
|
void |
savePluginMap()
Save the current plugin map from the memory to a file specified by the
EXTERNAL_PLUGIN_XML variable. |
void |
setEnabled(Plugin plugin,
boolean enabled)
Enable or disable a plugin.
|
boolean |
uninstallPlugin(Plugin plugin)
Uninstall an external plugin.
|
public static final String EXTERNAL_PLUGIN_XML
public static final String EXTERNAL_LEGACY_PLUGIN_XML
public static final String INTERNAL_PLUGIN_XML
public static final String XML_PLUGINGROUP
public static final String XML_PLUGIN
public static final String XML_APPLICATION
public static final String XML_GROUP_NAME
public static final String XML_GROUP_NAME_KEY
public static final String XML_INTERFACE
public static final String XML_ENABLED
public static final String XML_SOURCE
public static String getKnownGroupName(String group)
group
- a group ID.public static Map<String,String> getKnownGroups()
public Map<String,File> getJavaTestScriptMap()
public static PluginManager getInstance()
public boolean uninstallPlugin(Plugin plugin)
Uninstall an external plugin. This is not applicable to internal built-in
plugins which cannot be uninstalled. The method removes the plugin from the map of plugins
in the memory. If the plugin is loaded from a library which is not needed
any more (i.e. there's no other plugin installed from the library), it is
removed from the internal list of libraries and also from the application
Java class path. Successful uninstallation of a plugin fires a plugin event
with the PluginEvent.PLUGIN_UNINSTALLED
code to all registered listeners.
After the plugin is uninstalled, the method checks the default
plugin map for any built-in plugin providing the same functionality (with
the same code returned by getCode()
) and enables
it automatically if it exists. This logic is not applied to external plugins
and enabling of an already installed external plugin must be done
programatically.
The method doesn't save plugin map changes to the file system.
Users calling this method programatically have to call the savePluginMap()
method afterwards.
plugin
- a plugin. If the argument is null or it represents an internal
built-in plugin, the method does nothing and returns false.public List<PluginInfo> getPlugins()
PluginInfo
instances).public boolean isInstalled(Plugin p)
p
- a plugin.public boolean isEnabled(Plugin p)
setEnabled(com.tplan.robot.plugin.Plugin, boolean)
method documentation for more information.p
- a plugin.public boolean isBuiltIn(Plugin plugin)
plugin
- a plugin.public void setEnabled(Plugin plugin, boolean enabled) throws CodeConflictException
Enable or disable a plugin. A disabled plugin is not visible to other classes
(through plugin factories) but remains installed and can be reenabled any time.
If the method is called to enable an already enabled plugin or disable an
already disabled one, it does nothing. Any change of the plugin status
fires a plugin event with the PluginEvent.PLUGIN_ENABLED
or
PluginEvent.PLUGIN_DISABLED
code to all registered listeners.
Disabling of plugins is used in case of plugin reimplementations, which means installation of a plugin which replaces functionality of a built-in plugin or an already installed external one. The method can be however also used to disable plugins programatically at a runtime.
If an external plugin is being disabled, the method checks the default
plugin map for a built-in plugin providing the same functionality (with
the same code returned by getCode()
) and enables
it automatically if it exists. This logic is not applied to external plugins
and enabling of an already installed external plugins must be done
programatically.
plugin
- a plugin (Plugin
instance).enabled
- true enables functionality of the plugin, false disables.CodeConflictException
- when the plugin can
not be enabled for a dependency error or conflict.public List<PluginInfo> getPlugins(Class implementedInterface, boolean includeDisabled)
implementedInterface
- class of the exposed functional interface
implemented by the plugin.includeDisabled
- false returns just the enabled plugin, true lists
all plugins regardless whether they are enabled or disabled.public List<PluginInfo> getPlugins(Object code, Class implementedInterface, boolean includeDisabled)
code
- plugin code.implementedInterface
- exposed functional interface implemented by the plugin.includeDisabled
- true will include disabled plugins, false lists just enabled ones.PluginInfo
instances) which return
the specified plugin code and implement the specified interface.public Map<Class,String> getInterfaceMap()
public Map<Class,String> getInterfaceKeyMap()
public void savePluginMap() throws FileNotFoundException
Save the current plugin map from the memory to a file specified by the
EXTERNAL_PLUGIN_XML
variable. Format of this XML file is specified
in description of this class.
Note that saving of the plugin map is not performed automatically and users who modify the plugin map from custom programs must call this method to save their changes.
FileNotFoundException
- when the file cannot be opened for
writing.public void installPlugin(String pluginClassName, String libraryUrl, boolean force, boolean enable) throws InstantiationException, IllegalAccessException, IOException, ClassNotFoundException, UnsupportedVersionException, HigherVersionInstalledException, CodeConflictException, DependencyMissingException, MalformedURLException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException
Install and eventually enable an external plugin.The method adds the plugin to the
internal plugin map and if the enable
flag is set to true, it
makes it enabled and thus immediatelly visible to all plugin
factories.If the plugin library is not present on the Java class path,
it is dynamically added and all its classes are made available to the default
JVM class loader.Successful installation of a plugin fires a plugin event
with the PluginEvent.PLUGIN_INSTALLED
code to all registered listeners.
When value of the force
argument is false, the method
supports the following standard error scenarios:
Plugin.getCode()
) will
throw a CodeConflictException
. To override it recall the method with
the force
set to true. The method then disables the other plugin
and installs and enables the new one. For more information on plugin disabling
see the setEnabled(com.tplan.robot.plugin.Plugin, boolean)
method.HigherVersionInstalledException
. For this purpose two plugins
are considered to be the same if they have the same unique ID
.
This cannot be overriden by the
force
flag. To proceed with the downgrade one has to uninstall
the plugin first using uninstallPlugin(com.tplan.robot.plugin.Plugin)
and then install the new one. Internal built-in plugins
cannot be downgraded.UnsupportedVersionException
is thrown. This cannot
be overriden by the force
flag and T-Plan Robot Enterprise upgrade
to the required version must be performed before the plugin can be successfully
installed.DependencyMissingException
exception is thrown. This error can
be overriden by the force
flag. The plugin is however unlikely to work correctly
and installation of the missing plugins is highly recommended.Non-standard errors are reported through other exceptions declared by this method
such as InstantiationException
, IllegalAccessException
,
IOException
and ClassNotFoundException
. They
typically indicate an internal or misconfiguration error where the plugin
class (library) is missing, it cannot be instantiated or added to the plugin map.
The method doesn't save plugin map changes to the file system.
Users calling this method programatically have to call the savePluginMap()
method afterwards.
pluginClassName
- full plugin class name, for example "mypackage.MyPlugin".libraryUrl
- URL of the library which contains the plugin class.force
- true overrides some standard error situations. See the method description.enable
- true enables the plugin after installation, false just installs
and leaves the plugin disabled.IOException
- when the map is not writable or the specified library is not available/readable due to an I/O error.ClassNotFoundException
- when class of a plugin specified in the map is not found.
It typically means that the plugin class or any of its dependencies are not present in the
class path of the current Java runtime.InstantiationException
- when the plugin can't be instantiated.
Each plugin must have a default parameterless constructor and must be instantiable.IllegalAccessException
- An IllegalAccessException is thrown when plugin manager tries
to create a plugin instance or invoke its method through the Java Reflection API, but the currently
executing method does not have access to the definition of
the specified plugin class, method or constructor.UnsupportedVersionException
- thrown when the plugin
requires higher T-Plan Robot Enterprise version than the current one.HigherVersionInstalledException
- thrown when a higher
version of the same plugin is already installed.CodeConflictException
- thrown when another
plugin providing the same functionality is already installed and enabled.DependencyMissingException
- when one or more dependencies (other plugins)
required by the plugin is not installed.MalformedURLException
- if the library URL is malformed.NoSuchMethodException
- on a Reflection error.InvocationTargetException
- on a Reflection error.IllegalArgumentException
public static boolean implementsInterface(Class cl, Class interf)
Class.getImplementedInterfaces()
the method also checks all
superclasses. It provides the same functionality as the "instanceof"
operator save that it avoids any class instantiation.cl
- a class to be checked for the implemented interface.interf
- an interface class.public List<PluginInfo> getAvailablePlugins(File file) throws IOException, ClassNotFoundException, IllegalAccessException
Plugin
interface.file
- a JAR/ZIP file or a directory with compiled Java classes.PluginInfo
instances describing available plugins.IOException
- when the input stream is not readable due to an I/O error.ClassNotFoundException
- when class of a plugin specified in the map is not found.
It typically means that the plugin class or any of its dependencies are not present in the
class path of the current Java runtime.IllegalAccessException
- An IllegalAccessException is thrown when plugin manager tries
to create a plugin instance or invoke its method through the Java Reflection API, but the currently
executing method does not have access to the definition of
the specified plugin class, method or constructor.public static List<String> getResources(Pattern pattern) throws IOException
IOException
public static List<String> getResources(String element, Pattern pattern) throws IOException
IOException
public static List<String> getResourcesFromDirectory(File directory, String directoryCanonicalPath, Pattern pattern) throws IOException
IOException
public static Collection<String> getResourcesFromJarFile(File file, Pattern pattern)
file
- a JAR or ZIP file.pattern
- a pattern to apply to the resource list.public static void addURL(URL u) throws IOException
u
- a JAR file or class path URL.IOException
- on an I/O error.public void addPluginListener(PluginListener listener)
listener
- a listener to be registered for plugin map changes.public void removePluginListener(PluginListener listener)
addPluginListener(com.tplan.robot.plugin.PluginListener)
method
for more information.listener
- a listener to be registered for plugin events.public boolean accept(File dir, String name)
accept
in interface FilenameFilter
public static void discoverJavaTestScripts(File f) throws IOException, ClassNotFoundException, IllegalAccessException
f
- a class path, JAR or ZIP file to search for Java test scripts.IOException
- on an I/O error while reading the file.ClassNotFoundException
- on a Reflection API error.IllegalAccessException
- on a Reflection API error.public static List<String> getInstalledPlugins()
public static PluginDescriptor getPlugin(String uniqueId) throws IOException
uniqueId
- the plugin unique ID.IOException
- on an I/O error.public static boolean isPlugin(File archive)
archive
- a ZIP archive.