Plugins are components or resources that can be hot plugged without restarting the product. They are installed in the DATADIR, and are therefore instance-wide.
To load your custom code in Exalead CloudView, it must be packaged as a CVPlugin. You can then easily upload it in the Administration Console.
Plugins can include special component classes, such as connectors, security sources, format converters, analysis processors, etc. They can also include other materials, such as resources (linguistics, statistics, etc.).
Each component class within a plugin can have its own configuration. For example, a connector, will usually have user-defined settings, which will be edited through the Administration Console > Connectors menu. The framework will handle:
The plugin/component framework allows you to package a set of components within a plugin. The plugin will typically be deployed from a standalone ZIP file including all the necessary JARs and resources.
What is a plugin
A plugin is a regular ZIP file using the 1989 PKZip original format, with deflate compression or no compression (beware to produce compatible files), and must have the following structure:
• META-INF/cvplugin.properties - can define several core properties related to the plugin, as key=value lines terminated by a line feed.
• lib/ - subdirectory contains the plugin JAR file(s). The JAR file(s) will be parsed by the framework to locate all classes, load the required ones, execute initialization steps, etc.
• Other sub-directories can be included to collect additional resources. We strongly recommend not to put any standalone file at the top level of the ZIP directory structure, and collect necessary material in a dedicated sub-folder, such as a resources/ sub-folder.
Description of the META-INF/cvplugin.properties file
Component
Description
plugin.jars
A space-separated list of JAR names to be parsed. If defined, the framework will not scan all the jar files present in the lib/ subdirectory, but only scan those defined in this list.
Example: plugin.jars=mycomponent.jar
plugin.mainClass
The main plugin class name. If defined, the framework will use the given class name instead of searching for a unique CVPlugin-derived class.
We recommend using the @CVPluginAuthor() annotation instead.
plugin.copyright
The copyright information.
We recommend using the @CVPluginCopyright annotation instead.
plugin.description
The description.
We recommend using the @CVPluginDescription annotation instead.
plugin.version
The version information.
We recommend using the @CVPluginVersion annotation instead.
Create a basic plugin
The following procedure describes the main steps to create a CVplugin on a Linux environment.
1. Go to your plugin directory:
2. Create the META-INF/ and lib/ subdirectories:
3. Create a cvplugin.properties file under the META-INF/ subdirectory:
4. Copy your JAR component in the lib/ subdirectory:
5. Zip your plugin:
About the CVPlugin public class
To execute specific initializations through the constructor, you should define a public main class extending the CVPlugin (com.exalead.mercury.plugin.CVPlugin) class.
CVPlugin class basics
This class must be included in one of the JAR files of the lib/ subdirectory. All classes within the JAR collection are available by default through the plugin class loader.
If no such class exists, and if there is only one component within the plugin, the framework will use metadata associated with this unique component.
The name of the CVPlugin-derived class is not important. There should be at most one CVPlugin-derived class in the classes collection.
/** My plugin. **/ @CVPluginDescription("My Wonderful Plugin") @CVPluginVersion("1.0") @CVPluginCopyright("Copyright Wonders & Co., All Rights Reserved") @CVPluginAuthor("Wonders & Co.") public class Plugin extends CVPlugin { protected final File installDirectory;
/** Default constructor. **/ public Plugin(final String name, final File installDirectory) { super(name, installDirectory); this.installDirectory = installDirectory; // Optionally, define the plugin initialization code here: } }
CVPlugin class annotations
This CVPlugin-derived plugin class may define a number of useful annotations (located in the com.exalead.mercury.plugin package) to provide additional information.
Annotation
Description
@CVPluginAuthor
The plugin author name(s).
@CVPluginCopyright
The plugin copyright information.
@CVPluginDescription
The plugin description.
@CVPluginVersion
The plugin version information.
Top level component class(es)
A component is a class implementing the com.exalead.mercury.component.CVComponent interface.
This top-level interface is an empty interface (no defined method inside) designed to be automatically detected by the framework.
All available non-inner classes implementing this interface are collected during startup while scanning all JARs, and allow to list a subset of components implementing a specialized class or interface at runtime. For example, all components deriving from the com.exalead.papi.framework.connectors.Connector class are listed to collect the list of available connectors in the product.
If specified, this is the label of the component. The label is used in the Administration Console when selecting a custom document processor, semantic processor, or query prefix handler.
For example: @CVComponentLabel("My document processor")
CVComponentIntrospector (com.exalead.mercury.component.CVComponentIntrospector) derived co-class used for introspection queries, for example the Check connectivity operation available in the Administration Console.
and a list of supported query classes derived from SupportedQuery (com.exalead.mercury.component.SupportedQuery).
A configuration class defines a list of configuration properties that can be used by a component.
The framework will usually:
• manage the serialized configuration (as an XML object),
• unserialize it,
• create an instance of the given class, and allow you to edit its properties,
• handle the configuration of components from the Administration Console.,
• etc.
The exact workflow is specific to each component type. For example, some component types may not have any configuration at all.
A configuration class must implement the empty CVComponentConfig (com.exalead.mercury.component.config.CVComponentConfig) interface to be accepted as a valid configuration class.
Example:
/** * The configuration class for the filesystem connector. **/ @CVComponentDescription("Filesystem simple demo (java)") public class DemoFileSystemConnectorConfig extends ConnectorConfig { private File startingFolder = null;
public File getStartingFolder() { return startingFolder; }
/** * Optional method listing all option names as they will be displayed in the UI **/ public static String[] getMethods() { return new String[] { "StartingFolder" }; } }
Note: An additional static method named getMethods may be defined in a configuration class, to return properties in a specific order. This method should be public, taking no argument, and returning an array of String corresponding to the ordered property names.
Example :
public static String[] getMethods() { return new String[]{"Proxy", "ProxyPort"}; }
public class SamplifyConnectorSampleConfig extends ConnectorConfig implements CVComponentConfigSamplify { @IsMandatory(true) public void setPrimaryServer(final WebServer primaryServer) { this.primaryServer = primaryServer; } public WebServer getPrimaryServer() { return this.primaryServer; }
@IsMandatory(false) public void setSecondaryServers(final WebServer[] secondaryServers) { this.secondaryServers = secondaryServers; } public WebServer[] getSecondaryServers() { return this.secondaryServers; }
@Override public void samplify() { // samplify is called after the config instantiation. this.primaryServer.setServer("mywebserver.mydomain"); this.primaryServer.setPort(80); // Thus there will be a default value for the primary server name // but it won't appear when adding a new secondary server }
// a webserver is made of a server name and a network port public static class WebServer { @IsMandatory(true) public void setServer(final String server) { this.server = server; } public String getServer() { return this.server; }
@IsMandatory(true) public void setPort(final int port) { if (port < 0 || port > 65535) { final ConfigurationException e = new ConfigurationException("Invalid network port: " + port); e.setConfigKey("Port"); throw e; } this.port = port; } public int getPort() { return this.port; }
public static String[] getMethods() { return new String[] { "Server", "Port" }; }
private String server; private int port; }
private WebServer primaryServer = new WebServer(); private WebServer[] secondaryServers;
public static String[] getMethods() { return new String[] { "PrimaryServer", "SecondaryServers" }; } }
The UI should be similar to the following screenshot:
Setter/Getter methods
This section describes the setter and getter methods when packaging your CVPlugin.
About setters
Setters are:
• public methods that do not return any value (void return type),
• whose names are prefixed by set (the property names following the set prefix keep the same letter case),
• and take exactly one argument (the value to be set).
Setters may bear annotations, see Setter function annotations. Setters annotated with the IsHidden (com.exalead.config.bean.IsHidden) class are ignored.
These are the setters for the Proxy and ProxyPort properties.
@IsMandatory(false) @PropertyLabel("Proxy to use") public void setProxy(final String proxy) { this.proxy = proxy; } @IsMandatory(false) @PropertyLabel("Proxy port to use") public void setProxyPort(final int port) { this.port = port; }
Note: The first upper case is kept for the property name.
Each setter method should be associated with a getter method.
About getters
Getters are:
• public methods returning a value,
• the returned value should be equivalent to the value taken as argument in the setter,
• and take no argument.
Getters do not bear any annotation.
These are the getters for the Proxy and ProxyPort properties.
public String getProxy() { return proxy; } public int getProxyPort() { return proxy; }
Note: The first upper case is kept for the property name.
The available types, recognized by the framework for setters and getters are the following ones:
• Any class supporting the public static valueOf method taking exactly one String argument, and returning an object of its own class (in this case, the toString method from the Object base class will also be used); such as enums classes.
• Any class taking exactly one String argument (in this case, the toString method from the Object base class will also be used); such as the Date class.
• Generic Interface class (to accept outer components in configurations; used internally only).
• An array of an allowed type.
Example of array parameters:
public class NotesServerConfig implements CVComponentConfigSamplify { public static class NotesDBRule { public NotesDBRule(final String rule) { this.rule = rule; } public NotesDBRule() { }
protected String rule; public String getRule() { return rule; } @Override public String toString() { return getRule(); } }
private NotesDBRule[] includes = new NotesDBRule[] {};
Displays a combo box to list available classes in all configured data models. The data models to search classes in can be restricted with the following options (both options are mutually exclusive):
• dataModel: only list classes of the given data model.
• buildGroup: only list classes of the data model referenced by the given build group.
@Date (com.exalead.config.bean.Date)
Displays a calendar to configure the property field. The property field will be stored as a string formatted with the date format specified in the annotation.
Default date format is: @Date(format=“yyyy-MM-dd”)
Example: If you add a startDate field with
@Date(format=“yyyy-MM-dd”) public void setStartDate(String startDate) { this.startDate = startDate; }
The UI will display: Start date: [calendar widget]
And when dates are saved, they will be stored with the yyyy-MM-dd date format.
@DateTime (com.exalead.config.bean.DateTime)
Same as @Date with time information.
Default datetime format is: @DateTime(format=“yyyy-MM-dd HH:mm:ss”)
If the associated boolean is true, then the given setter property must be defined (non-empty unserialized value) so that the configuration can be considered as valid.
Otherwise, the property will be considered as optional.
Note: Without this annotation, the default is mandatory.
Displays a combo box to list configured security sources. The list of security sources can be restricted with the following flags:
• allowSelf: if the component describes a security source, indicates whether this security source is included in the combobox (default is false)
• allowDeployed: indicates whether deployed security sources are listed (default is true)
• allowUndeployed: indicates whether undeployed security sources are listed (default is true)
• allowedClassId: a regular expression to filter by security source classid. If the regular expression is an empty string, then all security sources are listed.
• forbiddenClassId: a regular expression to exclude security sources by classid.
• allowEmpty: adds an extra empty option in the combo box. Property value will be "" when this option is selected.
Displays a text input that performs remote queries to suggest entries. In order to remote suggestions to work, the CVComponent must support SuggestOption queries. Here is a full example:
class MyComponentIntrospector implements CVComponentIntrospector { public Object execute(CVComponentConfig componentConfig, IntrospectionQuery query) throws Exception { if (query instanceof SuggestOption) { SuggestOption suggestQuery = (SuggestOption) query; SuggestOption.SuggestResult result = new SuggestOption.SuggestResult();
// disambiguate by field name if you have several fields annotated with @WithSuggest if ("Folder".equals(suggestQuery.getConfigKey())) { String currentValue = suggestQuery.getCurrentValue(); MyComponentConfig myConfig = (MyComponentConfig) componentConfig; String[] suggestedValues = ...; result.setSuggestedValues(suggestedValues); }
return result; } throw new IllegalStateException("Unknown introspection query"); } }
Available classes for setters and getters
The following classes can be used in setters and getters.
A value representing a tri-state Boolean value, with checked value (true, false or optional)
KeyValue (com.exalead.config.bean.KeyValue)
A value representing a pair of strings (key and a value).
Possible Setter exceptions
A setter may raise exceptions, such as NumberFormatException or IllegalArgumentException, but we strongly recommend using the following built-in exceptions.