/**
* Copyright (c) 2009--2013 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package com.redhat.rhn.common.messaging;
import com.redhat.rhn.frontend.events.SsmRemovePackagesAction;
import com.redhat.rhn.frontend.events.SsmRemovePackagesEvent;
import com.redhat.rhn.frontend.events.SsmUpgradePackagesAction;
import com.redhat.rhn.frontend.events.SsmUpgradePackagesEvent;
import EDU.oswego.cs.dl.util.concurrent.Channel;
import EDU.oswego.cs.dl.util.concurrent.LinkedQueue;
import com.redhat.rhn.frontend.events.CloneErrataAction;
import com.redhat.rhn.frontend.events.CloneErrataEvent;
import com.redhat.rhn.frontend.events.NewCloneErrataAction;
import com.redhat.rhn.frontend.events.NewCloneErrataEvent;
import com.redhat.rhn.frontend.events.NewUserAction;
import com.redhat.rhn.frontend.events.NewUserEvent;
import com.redhat.rhn.frontend.events.RestartSatelliteAction;
import com.redhat.rhn.frontend.events.RestartSatelliteEvent;
import com.redhat.rhn.frontend.events.SsmChangeBaseChannelSubscriptionsAction;
import com.redhat.rhn.frontend.events.SsmChangeBaseChannelSubscriptionsEvent;
import com.redhat.rhn.frontend.events.SsmChangeChannelSubscriptionsAction;
import com.redhat.rhn.frontend.events.SsmChangeChannelSubscriptionsEvent;
import com.redhat.rhn.frontend.events.SsmConfigFilesAction;
import com.redhat.rhn.frontend.events.SsmConfigFilesEvent;
import com.redhat.rhn.frontend.events.SsmDeleteServersAction;
import com.redhat.rhn.frontend.events.SsmDeleteServersEvent;
import com.redhat.rhn.frontend.events.SsmErrataAction;
import com.redhat.rhn.frontend.events.SsmErrataEvent;
import com.redhat.rhn.frontend.events.SsmPowerManagementAction;
import com.redhat.rhn.frontend.events.SsmPowerManagementEvent;
import com.redhat.rhn.frontend.events.TraceBackAction;
import com.redhat.rhn.frontend.events.TraceBackEvent;
import com.redhat.rhn.frontend.events.UpdateErrataCacheAction;
import com.redhat.rhn.frontend.events.UpdateErrataCacheEvent;
import com.redhat.rhn.frontend.events.SsmInstallPackagesAction;
import com.redhat.rhn.frontend.events.SsmInstallPackagesEvent;
import com.redhat.rhn.frontend.events.SsmSystemRebootAction;
import com.redhat.rhn.frontend.events.SsmSystemRebootEvent;
import com.redhat.rhn.frontend.events.SsmVerifyPackagesAction;
import com.redhat.rhn.frontend.events.SsmVerifyPackagesEvent;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* A class that passes messages from the sender to an action class
*
*/
public class MessageQueue {
/**
* Logger for this class
*/
private static Logger logger = Logger.getLogger(MessageQueue.class);
private static final Map<Class, List<MessageAction>> ACTIONS =
new HashMap<Class, List<MessageAction>>();
private static Channel messages = new LinkedQueue();
private static Thread dispatcherThread = null;
private static MessageDispatcher dispatcher = null;
private static int messageCount;
/**
* Util class so we don't have a usable constructor
*/
private MessageQueue() {
}
/**
* Publish a new message
* Each message is wrapped in a ActionExecutor instance
* @param msg EventMessage to publish to queue.
*/
public static void publish(EventMessage msg) {
if (logger.isDebugEnabled()) {
logger.debug("publish(EventMessage) - start: " + msg.getClass().getName());
}
if (!isMessaging()) {
startMessaging();
}
if (msg != null) {
synchronized (ACTIONS) {
List<MessageAction> handlers = ACTIONS.get(msg.getClass());
if (handlers != null && handlers.size() > 0) {
logger.debug("creating ActionExecutor");
ActionExecutor executor = new ActionExecutor(handlers, msg);
try {
messages.put(executor);
messageCount++;
}
catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
else {
logger.debug("handlers is null, not processing!");
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("publish(EventMessage) - end");
}
}
static Runnable popEventMessage() throws InterruptedException {
Runnable retval = (Runnable) messages.poll(500);
if (retval != null) {
synchronized (ACTIONS) {
messageCount--;
}
}
return retval;
}
/**
* Start the messaging system
*/
public static synchronized void startMessaging() {
if (logger.isDebugEnabled()) {
logger.debug("startMessaging() - start");
}
if (isMessaging()) {
return;
}
dispatcher = new MessageDispatcher();
dispatcherThread = new Thread(dispatcher);
dispatcherThread.setName("RHN Message Dispatcher");
dispatcherThread.setDaemon(false);
dispatcherThread.start();
if (logger.isDebugEnabled()) {
logger.debug("startMessaging() - end");
}
}
/**
* Stop the messaging system
*/
public static synchronized void stopMessaging() {
if (logger.isDebugEnabled()) {
logger.debug("stopMessaging() - start");
}
dispatcher.stop();
if (logger.isDebugEnabled()) {
logger.debug("stopMessaging() - end");
}
}
/**
* Get the number of messages in the queue
* @return int number of messages in queue.
*/
public static int getMessageCount() {
return messageCount;
}
/**
* Register an action
* @param act MessageAction
* @param eventType type of event.
*/
public static void registerAction(MessageAction act, Class eventType) {
if (logger.isDebugEnabled()) {
logger.debug("registerAction(MessageAction, Class) - : " + act +
" class: " + eventType.getName());
}
synchronized (ACTIONS) {
List<MessageAction> handlers = ACTIONS.get(eventType);
if (handlers == null) {
handlers = new ArrayList<MessageAction>();
ACTIONS.put(eventType, handlers);
}
handlers.add(act);
}
}
/**
* De-register an action
* @param act MessageAction.
* @param eventType Type of event.
*/
public static void deRegisterAction(MessageAction act, Class eventType) {
if (logger.isDebugEnabled()) {
logger.debug("deRegisterAction(MessageAction, Class) - start");
}
synchronized (ACTIONS) {
List<MessageAction> handlers = ACTIONS.get(eventType);
handlers.remove(act);
}
if (logger.isDebugEnabled()) {
logger.debug("deRegisterAction(MessageAction, Class) - end");
}
}
/**
* Get list of String Classnames of the registered Actions. For Managment
* of the MessageQueue and testability.
* @return String[] array of registered events.
*/
public static String[] getRegisteredEventNames() {
if (logger.isDebugEnabled()) {
logger.debug("getRegisteredEventNames() - start");
}
String[] retval = null;
synchronized (ACTIONS) {
if (ACTIONS.keySet().size() > 0) {
retval = new String[ACTIONS.keySet().size()];
int index = 0;
for (Iterator<Class> iter = ACTIONS.keySet().iterator(); iter.hasNext();) {
Class klazz = iter.next();
retval[index] = klazz.getName();
index++;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("getRegisteredEventNames() - end - null");
}
return retval;
}
/**
* Check to see if the MessageQueue is running and available to
* publish MessageEvents to
* @return boolean true if MessageQueue is running.
*/
public static boolean isMessaging() {
return (dispatcher != null && !dispatcher.isStopped());
}
/**
* Configures defaut messaging actions needed by RHN
* This method should be called directly after <code>startMessaging</code>.
*
*/
public static void configureDefaultActions() {
// Register the Actions for the Events
// If we develop a large set of MessageEvents we may want to
// refactor this block out into a class or method that
// reads in some configuration from an XML file somewhere
MessageQueue.registerAction(new TraceBackAction(), TraceBackEvent.class);
MessageQueue.registerAction(new NewUserAction(), NewUserEvent.class);
// this is to update the errata cache without blocking the login
// for 40 seconds.
MessageQueue.registerAction(new UpdateErrataCacheAction(),
UpdateErrataCacheEvent.class);
// Used for asynchronusly restarting the satellite
MessageQueue.registerAction(new RestartSatelliteAction(),
RestartSatelliteEvent.class);
// Used to allow SSM channel changes to be run asynchronously
MessageQueue.registerAction(new SsmChangeBaseChannelSubscriptionsAction(),
SsmChangeBaseChannelSubscriptionsEvent.class);
MessageQueue.registerAction(new SsmChangeChannelSubscriptionsAction(),
SsmChangeChannelSubscriptionsEvent.class);
MessageQueue.registerAction(new SsmDeleteServersAction(),
SsmDeleteServersEvent.class);
// Used to allow SSM package installs to be run asynchronously
MessageQueue.registerAction(new SsmInstallPackagesAction(),
SsmInstallPackagesEvent.class);
MessageQueue.registerAction(new SsmRemovePackagesAction(),
SsmRemovePackagesEvent.class);
MessageQueue.registerAction(new SsmVerifyPackagesAction(),
SsmVerifyPackagesEvent.class);
MessageQueue.registerAction(new SsmUpgradePackagesAction(),
SsmUpgradePackagesEvent.class);
// Used to allow SSM power management actions to be run asynchronously
MessageQueue.registerAction(new SsmPowerManagementAction(),
SsmPowerManagementEvent.class);
//Clone Errata into a channel
MessageQueue.registerAction(new CloneErrataAction(),
CloneErrataEvent.class);
MessageQueue.registerAction(new NewCloneErrataAction(),
NewCloneErrataEvent.class);
MessageQueue.registerAction(new SsmErrataAction(),
SsmErrataEvent.class);
// Misc
MessageQueue.registerAction(new SsmSystemRebootAction(),
SsmSystemRebootEvent.class);
// Deploy configuration files
MessageQueue.registerAction(new SsmConfigFilesAction(),
SsmConfigFilesEvent.class);
}
}