RHQ has a plugin development API called the Advanced Management Plugin System (AMPS).
AMPS has been built from the ground up to provide a easy-to-use yet powerful way to write, deploy and update plugins.
|Here are links to the SVN repository pointing to the source for the different APIs that plugin developers will need to use:|
If you want to get started quickly and dive right in writing your own custom plugin, we suggest beginning with the custom-plugin maven module skeleton. This includes a maven pom with dependencies defined, a starter set of resource components and a minimal rhq-plugin.xml plugin descriptor. This maven module can be found at http://git.fedorahosted.org/git/?p=rhq/rhq.git;a=tree;hb=master;f=etc/samples/skeleton-plugin. If you want to manage your own product through a JMX interface only, you might even be able to start off with the much simpler "custom-jmx-plugin" - it doesn't require any Java code to be written, just modify the plugin descriptor to refer to custom JMX MBeans you want to access. See http://git.fedorahosted.org/git/?p=rhq/rhq.git;a=tree;hb=master;f=etc/samples/custom-jmx-plugin for more.
We still recommend that you come back to this page and read it to fully understand AMPS.
There are five main "parts", or modules, that make up AMPS:
- Plugin Container (module core/plugin-container)
- Provides the main manager that contains all the plugins and the objects that manage them
- Runs inside the RHQ Agent or embedded in a managed resource directly, e.g. Embedded in the JBoss Admin Console
- The plugin writer does not need to be concerned with the classes in this module
- Plugin Components (module core/plugin-api)
- When the term "public plugin API" or "public AMPS API" is used, it typically refers to the interfaces in this module
- Contains component interfaces whose implementations will represent objects in the managed system
- Contains the interfaces for all facets a plugin can support (e.g. Measurement, Operation, Configuration, etc)
- Is simply an interface API - no implementations
- The plugin writer will write plugin components that implement these interfaces
- Domain Objects (module core/domain)
- Contains all the individual domain object POJOS - things like Resource, ResourceType, Configuration, etc.
- All other parts of the plugin system use the domain objects to model the managed system
- Plugins (module plugins)
- The out-of-box plugins that RHQ ships with
- These are the individual plugins that are loaded by the Plugin Container
- Each plugin provides the ability to manage a particular product
- The plugins implement one or more "components" from the Plugin Components module
- Look at the source code of these plugins to learn how to properly write plugins
- Native System (module core/native-system)
- Provides low-level JNI/native access to operating system information
- Allows you to get information from the OS process table to determine what things are running
- Allows you to execute external programs
- Provides metrics on operating system level details such as machine memory usage, CPU utilization, etc.
- Provides a Java-only fallback API for those platforms that currently do not have a JNI library available
The RHQ Agent provides the means by which the Plugin Container gets configured and initialized/shutdown. It is the agent's job to start the Plugin Container whereas its the Plugin Container's job to start the plugins. From the plugin writer's point of view, in order to deploy a new plugin, all that needs to be done is write a valid deployment descriptor and write the appropriate components that will communicate with the managed resource. Once the RHQ Agent and its Plugin Container is initialized, they can be considered a black box. The Plugin Container will handle all the classloading, threading, and running of the plugins.
The Plugin Container is the subsystem deployed within the RHQ Agent that loads and manages all of the plugins. It is to be considered by the plugin writer almost as if it is a non-existing entity. The plugin writer never explicitly interacts with the plugin container or its internal managers. The Plugin Container isn't technically part of the public AMPS API - there are no classes or interfaces within the Plugin Container module that the plugin writer has to implement or use. Now that you know what the Plugin Container is, you can forget about it - you do not need to know any details of it if you just want to write plugins.
A plugin's job is to manage and monitor one or more "products" - where a product can be a hardware box, a framework, embedded software or an external server or anything else that can be "managed". JBossAS application server is considered a "product"; RHQ ships with a JBossAS plugin to manage them. Apache Web Server, Hibernate, JBoss Portal and JGroups are all examples of "products" that can be managed by RHQ plugins. A "product" consists of one or more managed resources (or resources for short). Resources can be categorized as one of two generic types - servers or services. For example, the JBossAS "product" consists of a JBossAS server resource which has many service resources as children, such as Data Source services, JMS Queue/Topic services, EJB Session Bean services and more. From this point forward, the term "resource" will be used in lieu of the term "product" since a set of "resource" objects is how a plugin models a product and is, therefore, the more appropriate term to use.
At its core, a plugin consists of a single jar file that contains component classes and an XML plugin descriptor file. The plugin descriptor provides the metadata that describes everything about the plugin and the resources it can manage. This includes the resource names and versions, plugin configuration that enables the plugin to connect to the resource and resource configuration that configure the resource's internal components.
A plugin descriptor is an XML file that follows a well-defined XML Schema definition. A plugin writer needs to understand the descriptor schema and the concepts it models, because it is through the definitions within this plugin descriptor that the product's management interfaces can be exposed through RHQ.
A plugin descriptor defines some or all of the following:
- the names of the resource types (i.e. a product's servers and services) that are supported by the plugin
- plugin configuration that define the settings the plugin components use to connect to the managed resources
- a set of metrics (aka measurement definitions) that define the types of data the managed resources emit. This is the data that RHQ uses to monitor the resource
- a set of operations that can be invoked to manipulate managed resources. "start" and "stop" are the two most common operations a plugin would want to expose, thus allowing a user to start or stop a managed resource
- resource configuration that define the actual configuration of the managed resources (which is different than the plugin configuration; plugin configuration tells the plugin components how to connect/talk to the managed resource whereas resource configuration is the actual configuration settings of the managed resource itself)
- child resources (that is, the descriptor can define a hierarchy of resource types where one resource of one resource type can be considered the parent of other resources of another type). JBossAS and their Data Source services are a prime example of this - a JBossAS server resource is of type "JBossAS Server" - it has child service resources of type "JBossAS Data Source Service".
Please see the AMPS-Plugin Descriptor page for more information.
|If you are upgrading your plugin code to include new features, you must make sure not to rename resource types unless you are willing to forgo backwards compatibility with the older plugin.|
At the heart of the public AMPS API are "plugin components" that the plugin writer implements. There are two basic types of components - a ResourceDiscoveryComponent and a ResourceComponent, which are described in the sections below. In effect, the plugin writer needs to only write (or reuse, if appropriate) these two component implementations for each type of resource that is to be supported.
|It is recommended that you read the javadocs for all classes within the public AMPS API (i.e. the core/plugin-api module). All interfaces are javadoced to help explain further what it is a plugin writer needs to implement in order to complete a fully functioning plugin. The public AMPS API javadoc is considered part of the public AMPS documentation and a more detailed extension to this documentation page.|
The discovery component is an implementation written by the plugin writer that performs the discovery of the actual managed resources. The discovery component's job is to scan the platform (that is, the machine the agent/plugin container/plugin is running on) and to report back what it finds. A discovery component is only responsible for finding resources that it directly is in charge of managing. That is to say, a JBossAS plugin discovery component is not responsible for discovering all Apache Web Servers - it only needs to find JBossAS resources (leave the discovery of Apache Web Servers to the Apache plugin).
A discovery component will be told to go hunt for resources at an appropriate time by the Plugin Container. When the Plugin Container asks the discovery component to go discover more resources, it will send in a ResourceDiscoveryContext object to the discovery component. This context contains all the information the component needs to perform its duties of finding and creating new resources. The discovery context is also used to inject resources into the discovery component, in the case where the Plugin Container was able to discover new resources on behalf of the discovery component. A Plugin Container can only auto-discover resources if the appropriate metadata is supplied to it via the plugin's descriptor.
A resource component is a plugin abstraction that represents an actual managed resource. A resource component is stateful whose lifecycle is managed by the Plugin Container.
The Plugin Container will start and stop a resource component at the appropriate times. When a resource component is started, it typically connects to its underlying resource (the managed resource it represents) and maintains that connection until it is stopped by the Plugin Container (this is an implementation detail that a plugin writer is free to change). A resource component implementation provides a way for the Plugin Container to ask for the availability of the managed resource ("is this resource up? or has it gone down?") and to access optional functionality facets that a plugin writer chooses to expose. More on facets below.
A "facet" is simply an optional piece of functionality that a plugin writer chooses to expose to the Plugin Container and ultimately to the RHQ system as a whole. A plugin writer is free to have his resource components implement some, all or none of these facets (obviously, the more facets that are implemented and exposed, the more powerful and useful the plugins become).
This "facet" should be implemented by the ResourceDiscoveryComponent class for types of Resources that wish to allow Resources to be manually added to inventory via the RHQ GUI. In addition, the corresponding server or service elements in the plugin descriptor must include the supportsManualAdd="true" attribute. Manual add can be a useful capability when a particular Resource cannot be auto-discovered for some reason.
This facet provides basic availability checking - is a managed resource up or down? When the plugin container needs to know if a resource is running or not, it will ask the resource component's availability facet. Unlike the other facets, the AvailabilityFacet is required to be implemented by all resource components. You are forced to implement it because the ResourceComponent interface extends the AvailabilityFacet. You can optionally use the asynchronous availability collector to perform the avail checking.
This facet exposes the capability for the component to collect measurement data from the managed resource and to report that data back to the server. For a measurement facet to work, the plugin must define one or more metric definitions for the resource component's resource type in the plugin descriptor. The resource component does not have to concern itself with how to schedule measurement collections and when it should collect the data. The only thing the MeasurementFacet requires the resource component to do is go out to the actual managed resource and collect the requested data. The Plugin Container will manage all measurement collection schedules and will only call into the resource component's MeasurementFacet when the time is appropriate and it will only ask for the metrics that need to be collected at that time.
The measurement facet is what provides the graphs of measurement data that you see in the RHQ GUI Console.
This facet provides the ability for a resource component to modify the configuration of the actual managed resource. When a resource component implements this facet, it is saying it has the capability to get the current configuration of the managed resource as well as be able to change it. As an example, the JBossAS Data Source Service resource component implements the ConfigurationFacet because it can report back to the user what the current settings are of that data source (e.g. its JDBC driver, its JNDI name, its connection pool size, etc) and it can allow the user to change those settings.
This facet allows the resource component to perform operations (aka control actions) on the managed resource itself. This allows, for example, the JBossAS Server resource component to provide the capability to start and stop the JBossAS server. Other examples of operations are being able to clear a connection pool for a data source, or ask a resource to empty a data cache. Whatever the managed resource can be told to do, a resource component can expose that as an operation to the RHQ user.
Some resource components can support the creation and deletion of child resources (for example, a resource component representing the JBossAS server can create and delete JBossAS data source services by creating and deleting *-ds.xml files). This facet exposes this functionality.
Resources may have content associated with them including deployed software or software parts and other content. This system can be used to inventory these software parts and to install and remove them. Examples of deployed content are RPMs on Linux, EARs and WAR applications or libraries and deployment files on JBoss Application Server. Plugins can support arbitrary types of content with this system
Resources may have additional files (aka "content") associated with it - configuration files, deployment files, etc. Those resources that have associated content can implement the ContentFacet to help create, delete and manage that content.
In order to support managed resources, there is some data that support organizations might wish to know about regarding a managed resource. For example, what are the contents of its log files, data files and configuration files. The Support facet will provide a hook into this "support and maintanence" view of the managed resource.
|This facet is currently under development and is to be considered experimental.|
Certain plugins need to perform some initialization immediately when they load and some cleanup when they unload. Such global initialization and shutdown of a plugin is performed by its lifecycle listener.
Plugin developers can implement org.rhq.core.pluginapi.plugin.PluginLifecycleListener which provides the developer the ability to allocate global resources needed by all plugin components and a place to clean up those resources.
Each plugin can optionally define one lifecycle listener. To do so, in the plugin descriptor, you use the "pluginLifecycleListener" attribute on the top-level <plugin> element.
Domain Objects are the building blocks of the AMPS management system. They include the basic pieces of the management inventory such as resources, resource types, configuration data, et. al.
Resource represents a single entity in inventory - it can be a platform, server or service. The semantics of what a platform, server or service is can be vague - as such, a Resource object encapsulates any resource no matter what categorization it is under. ResourceCategory is an enumeration that is associated with each Resource and indicates if the Resource is considered a platform, server or service. ResourceType represents types of resource instances that can be added to inventory.
ResourceTypes are defined by plugin descriptors. As you add more plugins to the plugin container, your system can understand and manage more types of resources. ResourceTypes are added to your system when you deploy new plugins. For example, deploy the JBossAS plugin and you can now manage JBossAS servers. Introduce the Tomcat plugin, and you can now manage Tomcast servers, in addition to those JBossAS servers.
Resources represent individual instances of managed products and are added to your system when they are auto-discovered by your plugins and imported, or manually created via the server GUI Console.
Plugin descriptors not only define the ResourceTypes that it understands, but it also organizes those types in a hierarchy. For example, a JBossAS EJB Service only runs inside of a JBossAS Server, thus, the JBossAS EJB Service ResourceType is a child type of the JBossAS Server ResourceType.
Plugins can hardcode their hierarchy directly in its descriptor by nesting <service> definitions inside <server> or other <service> definitions. You can also use one of the models of AMPS-Plugin Extensions to enhance and extend existing resource types from other plugin definitions.
Configuration is one of the larger APIs inside of the Domain Object module. It defines a highly extensible set of configuration settings that is associated with any resource. Configuration is so versatile, it is used in many places in RHQ where a set of configuration properties are required - such as plugin configuration to define how a plugin component can connect to a particular resource and operation parameters to define what arguments need to be passed when invoking a resource's operation.
Identity is a critical part of writing plugins to model and manage products. The plugin writer must deliver consistent identities for inventory elements that meet uniqueness requirements. Identities must also be consistent between discoveries. A major part of a resource's identity is defined by its "resource key". The resource keys are the critical part of a plugin developer's efforts. They must be able to reliably and uniquely identify a resource under varying conditions and have to be a natural key that can be detected any time a discovery is run with regard to past discoveries. Keys do not have to be globally unique, but they must be unique amongst its siblings in the resource hierarchy tree. In other words, all resources under a particular parent that are of the same resource type must have resource keys that are different from each other.
As an example, you can have a Postgres Server resource that has a child database resource with a key of "somekey" as well as a child user resource with a key of "somekey" because they are different resource types (one is a database type, the other a user type). Different plugins cannot step on each other as the plugin is part of the definition of the type and two plugins may even define the same resource type (though this is not recommended to avoid confusion). It is recommended that you keep keys simple (such as the Database name of "somekey") and depend on it being a child of a specific parent for further uniqueness in the hierarchy. This allows you to have two "Database" resources with a key of "somekey" as long as they are children of two different Postgres Server resources.
All plugins have access to a set of native libraries that allow plugin components to ask the underlying operating system for details about the machine on which the plugin is running. Some of these features are not available on all hardware and OS platforms - only those platforms that have the native libraries available will be able to support all features described below. However, for those platforms that do not have the native libraries available, there will still be a limited feature set available.
The plugins will have access to a SystemInfo object that is specific to the hardware/OS platform on which the plugin is running. Once a plugin obtains a SystemInfo object from its context (either ResourceDiscoveryContext or ResourceContext), it can make calls to that object which will call down into the native libraries to obtain the requested data from the operating system. If there are no native libraries available, the SystemInfo will be backed with a pure Java implementation of some, but not all, of the methods defined in the SystemInfo interface (see the JavaSystemInfo implementation of that interface). The methods that are not supported by the pure Java implementation will throw an UnsupportedOperationException.
One interesting feature the SystemInfo interface provides is the ability to probe the operating system's process table. This is useful for your ResourceDiscoveryComponent implementations because they can scan the list of running processes and try to determine if it can auto-detect a managed resource that it is tasked to discover.
Through the use of the ProcessInfoQuery object, you can find processes that match a given set of criteria, defined by the Process Info Query Language (PIQL). You can even set pre-defined PIQL queries in your plugin's descriptor via the process-scan tag to have the plugin container scan the process table on behalf of your plugin.
|Rather than repeat already documented information, refer to ProcessInfoQuery to learn more about the syntax and usage of PIQL.|
Normally, the plugins that you write are independent from other plugins - in fact, during runtime, they are isolated from other plugin jars via separate classloaders. However, there are instances where you need to have one plugin depend on another, for either their class definitions or resource type definitions. For example, if your product's management interface uses JMX, you could have your product's plugin depend on the JMX plugin in order to be able to re-use the EMS functionality provided by the JMX plugin. An other example is the case where Hibernate is embedded in a JBossAS server - the Hibernate plugin can depend on the JBossAS plugin so Hibernate can inject its metadata into the JBossAS resource type hierarchy (i.e. to show that Hibernate can be a child service of JBossAS).
To have one plugin extend another, you provide one or more depends elements to the <plugin> element within the plugin descriptor. A depends element defines a plugin that the defining plugin depends on. A dependent plugin will be deployed after all of the plugins it depends on have already been deployed. When you use the depends tag, you are defining a "required dependency" meaning that dependency must exist and be deployed in order to the depending plugin to successfully be deployed. There are "optional dependencies" that can be implicitly defined using the two extension models explained next.
There are two different ways you can extend other plugins and their resource types: the Embedded Extension Model and Injection Extension Model. They provide slightly different functionality and have different semantics - why you would use one over the other is described in more detail in the AMPS-Plugin Extensions page. Please refer to that page for more information on how you can extend other plugins and their resource types. Note that if you extend one plugin from another using one of these extension models, you are implicitly defining an optional dependency on the extended plugin. This means if that plugin to be extended does not exist, the extending plugin can still be deployed and run - it will just have some missing resource types due to the missing optional dependency. You can use depends together with one of the above extension models if you want to require your extended plugins to exist at deployment time. In other words, specifying a depends tag will change the optional dependency to a required dependency.
Once you've built a plugin, put your custom plugin .jar inside the RHQ Server at:
or upload them via the Plugins administration page.
|In early versions of RHQ, you had to put the plugins at jbossas/server/default/deploy/rhq.ear/rhq-downloads/rhq-plugins|
That's all you need to do. If the server is already running, in a few seconds it will auto-detect the new plugin (or, if you overwrote an existing plugin, it'll detect the updated plugin). At this point, your agents are free to download the new/updated plugin.
Note that if you wish, you can use the RHQ Server GUI to deploy the plugin. Select the Administration>SystemConfiguration>Plugins menu item and you'll see the plugins page where you can upload your plugin jar. This will allow you to remotely deploy a plugin jar, or if you do not have access to the server's file system.
Once you deployed your new custom plugin on the server, you can have your agents download the new plugin. If the agent already has an older version of your plugin, it can update itself by pulling down the new plugin and overwriting the old one with it.
If the agent has not been started yet, all you need to do is start it. The agent will automatically update its plugins at startup.
If the agent is already started, you can execute the "plugins update" prompt command. This will shutdown the plugin container, pull down any plugins that have been updated on the server, then restart the plugin container. If you've imported your agent into the RHQ environment, you can even use RHQ itself to tell the agent to update its plugins - just go to the agent's Operations tab in the server UI and execute the Update Plugins operation.
You can read more about writing plugins on these pages: