/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.communications.command.server;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;
import org.jboss.remoting.transport.ConnectorMBean;
import org.rhq.enterprise.communications.ServiceContainer;
import org.rhq.enterprise.communications.command.Command;
import org.rhq.enterprise.communications.command.CommandType;
import org.rhq.enterprise.communications.command.client.ClientCommandSender;
import org.rhq.enterprise.communications.command.client.ClientCommandSenderConfiguration;
import org.rhq.enterprise.communications.command.client.RemoteInputStream;
import org.rhq.enterprise.communications.command.client.RemoteOutputStream;
import org.rhq.enterprise.communications.i18n.CommI18NFactory;
import org.rhq.enterprise.communications.i18n.CommI18NResourceKeys;
/**
* The superclass for all command services. Command services are providers of {@link CommandType command types} - that
* is, they support the invocation of one or more {@link Command commands}.
*
* <p>Extending this class gives all command services the same interface, which is how the {@link CommandProcessor} can
* be extended to execute arbitrary commands.</p>
*
* <p>All command services are registered with the {@link CommandServiceDirectoryMBean command service directory}, which
* allows the command processor to find this service and its supported commands easily.</p>
*
* <p>Note that subclasses do not need define their own MBean interface; they need only inherit this service's MBean
* interface in order to plug into the command processor infrastructure.</p>
*
* <p>This class is typically used to process requests of the same kind of command, or perhaps different versions of the
* same kind of command. To facilitate easier support for multiple commands, see {@link MultipleCommandService}. Use
* that class if a command service expects to process many different kinds of commands. That is not to say direct
* subclasses of this class cannot support multiple commands - they can. However, it typically will involve the execute
* method containing a series of <code>if-then-else</code> statements to check the <code>instanceof</code> value of the
* incoming {@link Command} object.</p>
*
* @author John Mazzitelli
*/
public abstract class CommandService extends CommandMBean implements CommandServiceMBean {
/**
* If this service was registered by a container, that container's reference will be stored here.
*/
private ServiceContainer m_container = null;
/**
* Ensures that the name this command service is to be registered on is valid. See {@link KeyProperty} for more
* information on the valid key properties.
*
* <p>If the name is invalid, an exception is thrown and the service will not get registered.</p>
*
* @throws IllegalArgumentException if the name does not follow the required format needed to interact with the
* command processor
*
* @see MBeanRegistration#preRegister(MBeanServer, ObjectName)
*/
@Override
public ObjectName preRegister(MBeanServer mbs, ObjectName name) throws Exception {
if (!KeyProperty.TYPE_COMMAND.equals(name.getKeyProperty(KeyProperty.TYPE))) {
String errorMsg = CommI18NFactory.getMsgWithLoggerLocale().getMsg(
CommI18NResourceKeys.INVALID_CMD_SERVICE_NAME, name, KeyProperty.TYPE, KeyProperty.TYPE_COMMAND);
throw new IllegalArgumentException(errorMsg);
}
return super.preRegister(mbs, name);
}
/**
* If this service was registered by the {@link ServiceContainer}, that container's reference will be returned. If
* this service object has not yet been registered or registered by some other mechanism, this will return <code>
* null</code>.
*
* @return the server-side components container that registered this service
*/
public ServiceContainer getServiceContainer() {
return m_container;
}
/**
* When this service is added by the {@link ServiceContainer}, that container's reference will be set via this
* method. This allows this command service to obtain its container (in case this service needs things like the
* {@link ServiceContainer#getClientConfiguration() client configuration}.
*
* @param container
*/
public void setServiceContainer(ServiceContainer container) {
m_container = container;
}
/**
* Gets the connector providing our remote services. The returned connector is the object that is accepting from
* remote clients the commands that this command service processes.
*
* @return this service's associated connector
*
* @throws Exception if failed to get the connector
*/
protected ConnectorMBean getConnector() throws Exception {
ConnectorMBean connector;
connector = (ConnectorMBean) MBeanServerInvocationHandler.newProxyInstance(getMBeanServer(),
ServiceContainer.OBJECTNAME_CONNECTOR, ConnectorMBean.class, false);
return connector;
}
/**
* Returns the subsystem that this command service is registered under.
*
* @return the subsystem or <code>null</code> if not registered
*/
protected String getSubsystem() {
ObjectName obj_name = getObjectName();
String subsystem = null;
if (obj_name != null) {
subsystem = obj_name.getKeyProperty(KeyProperty.SUBSYSTEM);
}
return subsystem;
}
/**
* Returns the command service ID that this command service is registered under - will be <code>null</code> if not
* registered or was not registered with {@link ServiceContainer#addCommandService(CommandService)}.
*
* @return the command service ID or <code>null</code> if not registered
*/
protected CommandServiceId getCommandServiceId() {
ObjectName obj_name = getObjectName();
CommandServiceId id = null;
if (obj_name != null) {
String index = obj_name.getKeyProperty(KeyProperty.ID);
if (index != null) {
id = new CommandServiceId(index);
}
}
return id;
}
/**
* This is a convienence method for use by those subclass services that need to process incoming
* {@link RemoteInputStream remote input streams} that were received from a client. Remote input streams require a
* connection back to the client that sent the stream - this is so this service can pull down the input stream data.
* This method prepares the given remote input stream with a properly configured client command sender targeted to
* send commands back to the client that sent the remote input stream.
*
* @param remote_stream the stream to be prepared with a sender
*/
protected void prepareRemoteInputStream(RemoteInputStream remote_stream) {
String server_endpoint = remote_stream.getServerEndpoint();
ClientCommandSenderConfiguration config = getServiceContainer().getClientConfiguration();
// we know we only make synchronous calls with non-guaranteed commands, so disable guaranteed delivery
// setting the filename to null also ensures our sender doesn't try to access the spool file at all
config.commandSpoolFileName = null;
// ensure that we do not throttle the stream commands
config.enableQueueThrottling = false;
config.enableSendThrottling = false;
// do not do any polling
config.serverPollingIntervalMillis = 0;
// i don't think we ever do async calls to remote streams, but just in case,
// we can't interleave them, so make sure we make calls serially
config.maxConcurrent = 1;
ClientCommandSender sender = getServiceContainer().createClientCommandSender(server_endpoint, config);
sender.startSending();
remote_stream.setClientCommandSender(sender);
return;
}
/**
* This is a convienence method for use by those subclass services that need to process incoming
* {@link RemoteOutputStream remote output streams} that were received from a client. Remote output streams require
* a connection back to the client that sent the stream - this is so this service can push down the output stream
* data. This method prepares the given remote output stream with a properly configured client command sender
* targeted to send commands back to the client that sent the remote output stream.
*
* @param remote_stream the stream to be prepared with a sender
*/
protected void prepareRemoteOutputStream(RemoteOutputStream remote_stream) {
String server_endpoint = remote_stream.getServerEndpoint();
ClientCommandSenderConfiguration config = getServiceContainer().getClientConfiguration();
// we know we only make synchronous calls with non-guaranteed commands, so disable guaranteed delivery
// setting the filename to null also ensures our sender doesn't try to access the spool file at all
config.commandSpoolFileName = null;
// ensure that we do not throttle the stream commands
config.enableQueueThrottling = false;
config.enableSendThrottling = false;
// do not do any polling
config.serverPollingIntervalMillis = 0;
// i don't think we ever do async calls to remote streams, but just in case,
// we can't interleave them, so make sure we make calls serially
config.maxConcurrent = 1;
ClientCommandSender sender = getServiceContainer().createClientCommandSender(server_endpoint, config);
sender.startSending();
remote_stream.setClientCommandSender(sender);
return;
}
}