|This page provides information on developing and deploying server plugins.|
If you want information on agent plugin development, see Plugin Community.
Server plugins provide a way for a developer to extend the RHQ Server. Server plugins are typically free to perform any work they need to do within the RHQ Server in order to extend the RHQ Server's functionality.
The server plugin API is very different from the agent plugin API. Whereas the agent plugin API has a very formal, facet-based API that provides the plugin developer with a strict interface to the agent plugin container, the server plugin API is much looser, due to what we are trying to accomplish with providing server-side plugability.
Server plugins are designed to plug into and extend RHQ Server functionality. Because of this, server plugins will typically need wide-access to internal server components and resources. Because we do not know all the things that server plugin developers intend to do, most of the server internals will be available to them. This allows for those plugin developers to innovate within any area of the RHQ Server.
The one interface plugin developers usually will want to implement is org.rhq.enterprise.server.plugin.pc.ServerPluginComponent. This provides simple lifecycle management to the plugin. When a plugin container initializes, starts, stops and shuts down a plugin, the plugin component interface provides the hooks that allow the plugin to be notified of those lifecycle stages. The plugin component is a stateful object, remaining "alive" for the length of time that the plugin remains initialized. The plugin developer is free to perform any tasks or call any methods within those lifecycle methods to do the work they need to do. When a plugin is initialized, it is given a "server plugin context" that provides information about the runtime environment of the plugin. This object is useful to the plugin developer by providing things such as what the plugin's descriptor is, what its plugin configuration is, and where the plugin can write temporary or persisted data files, among other things.
The plugin developer informs the plugin container about the plugin component class by using the <plugin-component> element of the plugin's XML descriptor file.
There are some instances where you would like a user to directly interact with a server plugin's stateful component. In these cases, you can have your ServerPluginComponent class implement the ControlFacet interface. This allows the server plugin to expose "control operations" that the user can invoke directly from the user interface. You define what control operations your plugin supports via the <control> element, which is specified as a child element to the <plugin-component> element. You can have none, one or more control operations defined for your plugin component. You can also define optional parameters that the user can pass into the control operation, as well as optional result properties that the control operation will return when it completes.
One of the main capabilities that the server plugin container subsystem provides to plugins is the ability for plugins to define their own scheduled jobs and the plugin container will handle the actual scheduling and invocation of those jobs. The plugin simple tells the plugin container (via its deployment descriptor and configuration) what classes/methods should be invoked when the job is triggered, how often those jobs should be triggered (using a periodic timer or a "cron" timer) and what configuration settings or "callback data" should be passed to the job method when it is invoked. The job can perform any work it needs to get done when it is invoked. The job can have access to the plugin's stateful component, as well as information about the job itself via a org.rhq.enterprise.server.plugin.pc.ScheduledJobInvocationContext instance.
A job class can be stateless (meaning each job class is instantiated for each job invocation) or it can be stateful by invoking the plugin component instance.
A job can be "concurrent" or not. If a job is concurrent, that means more than one invocation can be performed at any one time on one or more RHQ Servers. If a job is not concurrent, that means one and only one job invocation can be performed at any time anywhere (i.e. you can't have one job running on one RHQ Server while at the same time have that job running on another RHQ Server, you also can't have two invocations of the same job running on the same RHQ Server).
There may be times when it is desirable to persist job-related data between job invocations. This is possible when a job is stateful. Note that this is not supported for stateless jobs. To make a job stateful, the concurrent property in the plugin descriptor must be set to false. Data can then be persisted and passed from one invocation to the next using the ScheduledJobInvocationContext as illustrated in the following example,
In this example we increment a simple counter with each invocation of the job. If the job was stateless, the counter would be reinitialized upon each invocation of the job, and its value would never increase past one. In addition to persisting data across job invocations, you may also want the job data to persist across server restarts as well. This can be accomplished by setting the clustered property to true in the plugin descriptor in addition to setting the concurrent property to false. ScheduledJobInvocationContext provides the following methods for accessing and updating job data.
|get(String key)||String||Retrieves a property value for the specified key or null if the key is not present|
|put(String key, String value)||void||Adds a property to the context|
|containsKey(String key)||boolean||Checks to see whether or not the key is present in the context|
|remove(String key)||String||Removes the property value along with the key. Returns the value or null if the key is not present.|
|getJobData||Map<String, String>||Returns a read-only map of all properties present in the context|
Note If the job is stateless and you invoke one of the methods in the previous table an exception is thrown. These methods are only usable for stateful jobs.
|The scheduling features within RHQ Server leverage the Quartz scheduling engine.|
The plugin descriptor is the mechanism that the plugin developer uses to tell the plugin container how the plugin should be deployed. First and foremost, the plugin descriptor indicates what type of plugin it is, and some basic information about the plugin, such as its name and version. But it also indicates which class is to be used as the plugin component (if one is declared at all). It indicates what, if any, jobs should be scheduled and the configuration of those scheduled jobs. It provides metadata on what the plugin configuration should look like, and what its default values should be.
It is advised that you read the rhq-serverplugin.xsd XML Schema annotation documentation, since it explains what all the different elements and attributes are.
Depending on the type of plugin you are writing, there are additional XML Schemas that your plugin descriptor needs to validate against. For example, if you are writing a content plugin, your descriptor can have additional data/metadata defined within it, according to the content XML Schema.
To see an example file descriptor, take a look at the sample server plugin descriptor.
There are no plugin-to-plugin dependencies. Unlike agent plugins, server-side plugins do not extend other plugins nor do they depend on other plugins. Each server-side plugin is independent from one another. There is no guarantee on the order in which server-side plugins are started or stopped.
There is a sample server plugin in the RHQ source found at etc/samples/custom-serverplugin. You can copy its maven pom.xml and file structure to quickly start developing your own server plugin. You need to provide an XML descriptor file in your plugin, as well as your plugin's Java classes and any other optional third-party libraries needed by your plugin's classes. If you need to ship with additional .jar libraries, be sure to put them in the "/lib" directory of your plugin .jar - all jars found there are placed in your plugin's classloader by the plugin container.
Server plugins are deployed in one of two ways.
- Use the Administration>SystemConfiguration>Plugins graphical UI to file-upload a server plugin jar.
- Copy the server plugin jar directly to the RHQ Server's "plugins dropbox" which is a directory located directly in the server's installation directory - it is called "<install-dir>/plugins".
In either case, once you give a copy of the plugin jar to the server, the server will auto-detect it within a few seconds to a few minutes (the actual time it takes will depend on the server configuration setting "rhq.server.plugin-scan-period-ms" located in rhq-server.properties. If you are a developer, you usually want to set this to something fast, like 30000ms, so it will never take more than 30 seconds to detect a hot-deployed plugin. In production, you'll want this to something higher, since rarely are new plugins deployed and you don't want the server using up I/O performing this check when things rarely change).
Once the new plugin is discovered by the server, it will be registered. The plugin will automatically be enabled, unless the plugin's descriptor told the plugin container to keep the plugin disabled, even after detection and registration. The descriptor attribute disabledOnDiscovery="true" is the flag that tells the plugin container to disable a plugin when it is discovered.
You can restart the "master plugin container" to get it to reload plugins. You can do this via the RHQ Server plugin's "Restart Master Configuration" operation found on the resource "RHQ Server Plugin Container" service resource, which is a child resource of the RHQ Server JBossAS server resource. This requires you to have imported the RHQ Server JBossAS server resource into inventory.
From the Administration>SystemConfiguration>Plugins GUI, you can manually enable and disable plugins as well. Disabling a plugin tells its plugin container to skip that plugin - it will not intialize or start that plugin.
From the same GUI page, you can also "undeploy" a plugin, which essentially deletes that plugin from the server. It will disappear from that GUI page (unless you elect to "Show Undeployed" plugins) and it will be forever "disabled".
If you undeploy a plugin, you are essentially telling the server you never want that plugin to be deployed again. However, if in the future you do wish to redeploy that plugin (or a future version of that plugin), you first must "purge" the old, undeployed plugin from the server. Once you purge the old plugin, you are free to upload a new version (or even the original version) of that purged plugin just as if it was a new plugin.
All server plugins are managed by a plugin container. There are several plugin containers in the system, all are responsible for managing a single type of plugin. The type of plugin you deploy will determine which plugin container will manage it. For example, if you write a "generic" server plugin, the "generic plugin container" will be the one to start and stop your plugin. If you write a "content" server plugin, the "content plugin container" will manage that plugin.
The RHQ Server has a fixed number of plugin containers available. If you have a need to write your own special type of server plugin, you'll need to write a new plugin container to handle that new type of plugin. Typically, you will not have to do this. If you need to write a server plugin that doesn't fit in with one of the specific types of plugins currently supported (e.g. "content" plugins, or "alert" plugins as examples), then you usually will just write a "generic" plugin, which will be managed by the catch-all generic plugin container. If you want to write your own plugin container, you'll have to follow some rules and guidelines specified here. Plugin containers are not meant to be "pluggable" into the RHQ Server - they are fixed and built directly into the RHQ Server. When you create new ones, you create them directly inside the core RHQ code. But again, this is rarely needed by typical server plugin developers since the generic plugin container will be able to handle most anything you'll want to cook up.
When you build your plugin, it would be nice to know if the plugin descriptor is correct prior to deploying it in the server. There is now a ServerPluginValidatorUtil class that can be used to validate one or more server plugins.
The base plugin validation does the following:
- Verifies the XML is well-formed and validates with the proper server plugin XML Schema
- Validates that, if a plugin component is specified, that its class is found in the plugin jar and it can be instantiated.
- Validates that all scheduled jobs are configured properly
For each specific type of plugin, additional validation rules are applied. Each type of plugin can have its own validator to apply its own rules to a plugin. Currently, the different validators are the following:
If you add more plugins to the RHQ master codebase, you need to remember to add the new plugin to the validate-all-serverplugins pom.xml - specifically, you have to add the new plugin jar to the test classpath of the validator java environment.