Agent RemotingThe remoting code used to talk from agent to server (and server to agent for that matter) has layers. The lowest layer involves JBoss/Remoting. Sitting on top of JBoss/Remoting is the RHQ concept of "commands" (i.e. "command" objects are sent to remote endpoints via jboss/remoting). But even that requires a bit more code for most people to want to do, so on top of the command framework sits the "remote pojo" framework. This "remote pojo" framework is how we layer true "remote RPC" semantics in the agent-server communications. This page will talk about all these layers and how, in the end, "*AgentService" and "*ServerService" interfaces allow you to make normal Java calls actually perform remote RPC calls. JBoss/RemotingThe communications layer used by the agent (and by the server as well) has JBoss/Remoting at its foundation. The RHQ comm layer builds on top of JBoss/Remoting - adding features like guaranteed delivery, throttling, and remote POJO invocations. Commands/Command Responses/Command ServicesDeep down in the RHQ comm layer (sitting on top of JBoss/Remoting) is the Command layer. All messages flowing into and out of the agent are Command objects. When a command reaches the remote server, it is processed by what is known as a Command Service which in turn responds with a Command Response object. This Command/CommandResponse framework is the basic building block upon which the other aspects of the communications framework is built upon. Remote POJO Invocation LayerSitting higher up in the comm stack is the remote POJO invocation layer. This layer provides most of the communications API used by the rest of the subsystems. Developers write their POJOs as a normal Java object, remote them via the RHQ Remoting subsystem and they immediately become available for remote clients to call. Using the remote POJO invocation layer allows development to proceed without the developer having to know anything about the remoting layer while at the same time being given functionality such as guaranteed delivery, asynchronous invocations, failover, etc. The RHQ Agent and RHQ Server both use this mechanism to access remote calls from each other. On the server, there are *ServerService interfaces, and on the agent there are *AgentService interfaces. More on this can be found in sections below. Writing new commands / command servicesHere's what you need to do to add new commands to the agent. Remember, these are low-level commands - usually, you won't have to do this if you are just adding new remote interfaces or new remote methods to existing remote interfaces. This section is here just for completeness.
You don't technically need Commands and Command Responses - you can use GenericCommand/GenericCommandResponse - however, these are weakly typed and requires you to perform casting to actually process the commands in a more specific way (as opposed to generically processing commands, like the CmdlineClient does). After these are defined, you add them to the agent. You can do this statically by adding your CommandService class name to communications.properties (the rhq.communications.command-services property). Or you can dynamically add command services at runtime. Requires rhq.communications.command-service-directory.allow-dynamic-discovery to be set to true in communications.properties (which it is in the default props file). Do add the services, you call ServiceContainer.addCommandService() passing in either a String (which is the classname of your service) or an Object (which is the command service instance). RPC / remote POJOsYou can write POJOs that are remoteable. Use ServiceContainer.addRemotePojo() to make your pojo instance remoted. Use ClientRemotePojoInvoker.getRemotePojo() to get a proxy to the remoted POJO. This floats over the command infrastructure (over the RemotePojoInvocationCommand/CommandResponse), thus giving it all the functionality that the command stuff provides (like setting timeout for commands, guaranteed delivery, concurrent, asynchronous sending, synchronous sending, etc. etc.) ServiceContainerThis singleton contains the server-side services. You can dynamically add new command services (so new commands can be processed) and you can add new network detection listeners so you can detect new servers (RHQ Agents or RHQ Servers both act as "comm servers") that come on and off line. ClientCommandSenderThis provides a client that can send commands to any remote server (RHQ Agent/RHQ Server). It can:
You can get an instance from the agent (AgentMain.getClientCommandSender() which sends the commands to the RHQ Server) or you can instantiate your own (requires you to know the remote endpoint or the RHQ Server or whatever remote server you want to send to).
PersistentFifoThere is a class that persists data in a FIFO queue in a single file - the class is org.rhq.enterprise.communications.command.client.PersistentFifo. There are a bunch of tests that prove it works It persists byte[] data - but its API allows you to persist objects and take objects from the queue (it simply (de)serializes the object):
You provide 3 things to the constructor - the File where the data is written, the maximum file size limit and a purge percentage. The maximum file size indicates how big we will allow the file to get before we need to shrink it somehow. If you put an entry on the FIFO, and that entry causes the file size to go over that max file size, this will trigger a purge. A purge will force the file to reduce in size such that it goes below the percentage of the max file size as defined by purge_percentage parameter (for example, if the maximum file size is 100KB and the purge percentage is 90, then a purge will shink the file down to no more than 90KB). A purge performs two things:
See the class javadocs for details on how this is implemented. I think we can use this for both my needs (to persist commands for guaranteed delivery) and for the plugin container to use if it wants to squirrel away metric data to a local spool file, for example. You can squirrel away double[] arrays using putObject and takeObject for example. This could be refactored to allow persisting the messages to a database, as opposed to a filesystem, if needed. AgentService and ServerService Remote InterfacesSitting on top of the remote POJO infrastructure are the AgentService and ServerService interfaces. These are not found in the comm module, but rather they are extensions built into the RHQ Agent and RHQ Server codebase that build agent-specific and server-specific capabilities needed by the agent and server. These are, essentially, the agent remote interfaces and server remote interfaces, as seen by each other. This is important to understand - these are not remote interfaces that other remote clients use (such as the CLI). These are strictly the contracts that the agent and server use to talk to each other. For example: org.rhq.core.clientapi.agent.configuration.ConfigurationAgentService is the RHQ Agent remote interface that the RHQ Server will use when it needs to talk to the agent's configuration subsystem. org.rhq.core.clientapi.server.operation.OperationServerService is the RHQ Server remote interface that the RHQ Agent will use when it needs to talk to the server's operation subsystem.. You will notice that all agent service remote interfaces are located in the core/client-api module in subpackages under org.rhq.core.clientapi.agent. All server service remote interfaces are located in the same core/client-api module in subpackages under org.rhq.core.clientapi.server.
Adding remote interfaces to the RHQ AgentMost of the time, you do not need to add an entirely new remote interface to the agent, because it is rare that you need to add a new subsystem. In most cases, you can simply add methods to existing remote interfaces (like ConfigurationAgentService) and that is all. Unless you have a good reason to add a new subsystem to the codebase, adding new methods to existing subsystem remote interfaces is what should be done. However, if you need to add a new subsystem to the agent, you have to complete a few things.
. Adding remote interfaces to the RHQ ServerMost of the time, you do not need to add an entirely new remote interface to the RHQ Server, because it is rare that you need to add a new subsystem. In most cases, you can simply add methods to existing remote interfaces (like OperationServerService) and that is all. Unless you have a good reason to add a new subsystem to the codebase, adding new methods to existing subsystem remote interfaces is what should be done. However, if you need to add a new subsystem to the RHQ Server, you have to complete a few things.
That's really all you need. The ServerServiceImpl objects will make calls into the SLSB layer to actually perform the work that the agent needs to get done.
. Comm AnnotationsThe core/comm-api module provides a few annotations that you can use to annotate your remote interfaces. These help you define additional behavior to your remote methods. See the org.rhq.core.communications.command.annotation package in the core/comm-api module for the different annotations you can use (these are javadoc'ed with additional information}. You should read the Javadocs for more details, but here's a summary:
Remote StreamsThis remote interface infrastructure supports the ability to do remote streaming. Your remote interfaces can pass InputStream and OutputStreams as parameters and return values. See the following as examples of this:
|