/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2009-2011 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) 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, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) 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 OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.ackd;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.EventConstants;
import org.opennms.netmgt.ackd.AckReader.AckReaderState;
import org.opennms.netmgt.ackd.readers.ReaderSchedule;
import org.opennms.netmgt.daemon.SpringServiceDaemon;
import org.opennms.netmgt.dao.AckdConfigurationDao;
import org.opennms.netmgt.model.OnmsAcknowledgment;
import org.opennms.netmgt.model.acknowledgments.AckService;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.model.events.EventForwarder;
import org.opennms.netmgt.model.events.annotations.EventHandler;
import org.opennms.netmgt.model.events.annotations.EventListener;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.springframework.beans.factory.DisposableBean;
/**
* Acknowledgment management Daemon
*
* @author <a href="mailto:david@opennms.org">David Hustace</a>
* @author <a href="mailto:jeffg@opennms.org">Jeff Gehlbach</a>
* @author <a href="mailto:david@opennms.org">David Hustace</a>
* @author <a href="mailto:jeffg@opennms.org">Jeff Gehlbach</a>
* @version $Id: $
*/
@EventListener(name=Ackd.NAME)
public class Ackd implements SpringServiceDaemon, DisposableBean {
/** Constant <code>NAME="Ackd"</code> */
public static final String NAME = "Ackd";
private volatile AckdConfigurationDao m_configDao;
private volatile EventForwarder m_eventForwarder;
private volatile ScheduledThreadPoolExecutor m_executor;
//FIXME change this to be like provisiond's adapters
private List<AckReader> m_ackReaders;
private AckService m_ackService;
private Object m_lock = new Object();
/**
* <p>start</p>
*/
public void start() {
log().info("start: Starting "+m_ackReaders.size()+" readers...");
startReaders();
log().info("start: readers started.");
}
/**
* <p>destroy</p>
*/
public void destroy() {
log().info("destroy: shutting down readers...");
try {
stopReaders();
m_executor.purge();
m_executor.shutdown();
//fairly arbitrary time (grin)
m_executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (Throwable e) {
log().error("destroy: error destroying readers."+e, e);
m_executor.shutdownNow();
}
log().info("destroy: readers shutdown.");
}
/**
* Starts the AckReaders without indicating a reload of their configuration is necessary.
*/
protected void startReaders() {
this.startReaders(false);
}
/**
* Starts the AckReaders indicating a reload of their configuration is necessary.
*
* @param reloadConfig a boolean.
*/
protected void startReaders(boolean reloadConfig) {
int enabledReaderCount = getConfigDao().getEnabledReaderCount();
if (enabledReaderCount < 1) {
log().info("startReaders: there are not readers enabled in the configuration.");
return;
}
m_executor.setCorePoolSize(enabledReaderCount);
log().info("startReaders: starting "+enabledReaderCount+" enabled readers of "+m_ackReaders.size()+" readers registered.");
for (AckReader reader : m_ackReaders) {
log().debug("startReaders: starting reader: "+reader.getName());
List<AckReaderState> allowedStates = new ArrayList<AckReaderState>();
allowedStates.add(AckReaderState.STOPPED);
try {
adjustReaderState(reader, AckReaderState.STARTED, allowedStates, reloadConfig);
} catch (Throwable e) {
log().error("startReaders: Could not start reader: "+reader.getName(), e);
continue;
}
log().debug("startReaders: reader: "+reader.getName()+" started.");
}
log().info("startReaders: "+m_ackReaders.size()+" readers started.");
}
/**
* <p>stopReaders</p>
*/
protected void stopReaders() {
log().info("stopReaders: stopping "+m_ackReaders.size()+" readers...");
for (AckReader reader : m_ackReaders) {
log().debug("stopReaders: stopping reader: "+reader.getName());
List<AckReaderState> allowedStates = new ArrayList<AckReaderState>();
allowedStates.add(AckReaderState.PAUSE_PENDING);
allowedStates.add(AckReaderState.PAUSED);
allowedStates.add(AckReaderState.RESUME_PENDING);
allowedStates.add(AckReaderState.RESUMED);
allowedStates.add(AckReaderState.STARTED);
allowedStates.add(AckReaderState.START_PENDING);
allowedStates.add(AckReaderState.STOP_PENDING);
try {
adjustReaderState(reader, AckReaderState.STOPPED, allowedStates, false);
} catch (Throwable e) {
log().error("startReaders: Could not stop reader: "+reader.getName(), e);
}
log().debug("stopReaders: reader: "+reader.getName()+" stopped.");
}
log().info("stopReaders: "+m_ackReaders.size()+" readers stopped.");
}
/**
* <p>pauseReaders</p>
*/
protected void pauseReaders() {
for (AckReader reader : m_ackReaders) {
List<AckReaderState> allowedStates = new ArrayList<AckReaderState>();
allowedStates.add(AckReaderState.STARTED);
allowedStates.add(AckReaderState.RESUMED);
try {
adjustReaderState(reader, AckReaderState.PAUSED, allowedStates, false);
} catch (Throwable e) {
log().error("startReaders: Could not pause reader: "+reader.getName(), e);
}
}
}
/**
* <p>resumeReaders</p>
*/
protected void resumeReaders() {
for (AckReader reader : m_ackReaders) {
List<AckReaderState> allowedStates = new ArrayList<AckReaderState>();
allowedStates.add(AckReaderState.PAUSED);
try {
adjustReaderState(reader, AckReaderState.RESUMED, allowedStates, false);
} catch (Throwable e) {
log().error("startReaders: Could not resume reader: "+reader.getName(), e);
}
}
}
/**
* <p>restartReaders</p>
*
* @param reloadConfigs a boolean.
*/
protected void restartReaders(boolean reloadConfigs) {
log().info("restartReaders: restarting readers...");
stopReaders();
startReaders(reloadConfigs);
log().info("restartReaders: readers restarted.");
}
private void adjustReaderState(AckReader reader,
AckReaderState requestedState, List<AckReaderState> allowedCurrentStates, boolean reloadConfig) {
synchronized (m_lock) {
if (!getConfigDao().isReaderEnabled(reader.getName())) {
//stop a disabled reader if necessary
if (!AckReaderState.STOPPED.equals(reader.getState())) {
log().warn("adjustReaderState: ignoring requested state and stopping the disabled reader: "+reader.getName()+"...");
reader.stop();
log().warn("adjustReaderState: disabled reader: "+reader.getName()+" stopped");
return;
}
log().warn("adjustReaderState: Not adjustingReaderState, disabled reader: "+reader.getName());
return;
}
if (allowedCurrentStates.contains(reader.getState())) {
log().debug("adjustReaderState: adjusting reader state from: "+reader.getState()+" to: "+requestedState+"...");
org.opennms.netmgt.config.ackd.ReaderSchedule configSchedule = getConfigDao().getReaderSchedule(reader.getName());
long interval = configSchedule.getInterval();
String unit = configSchedule.getUnit();
/**
* TODO: Make this so that a reference to the executor doesn't have to be passed in and
* the start method simply returns the task to be scheduled. The schedule can be adjusted
* by the AckReader. We just need to make sure that the future gets set in the AckReaer.
*/
if (AckReaderState.STARTED.equals(requestedState)) {
reader.start(m_executor, ReaderSchedule.createSchedule(interval, unit), reloadConfig);
} else if (AckReaderState.STOPPED.equals(requestedState)) {
reader.stop();
} else if (AckReaderState.PAUSED.equals(requestedState)) {
reader.pause();
} else if (AckReaderState.RESUMED.equals(requestedState)) {
reader.resume(m_executor);
} else {
IllegalStateException e = new IllegalStateException("adjustReaderState: cannot request state: "+requestedState);
log().error(e.getLocalizedMessage(), e);
throw e;
}
} else {
IllegalStateException e = new IllegalStateException("error adjusting reader state; reader cannot be change from: "+reader.getState()+" to: "+requestedState);
log().error(e.getLocalizedMessage(), e);
throw e;
}
}
}
/**
* Handles the event driven access to acknowledging <code>OnmsAcknowledgable</code>s. The acknowledgment event
* contains 4 parameters:
* ackUser: The user acknowledging the <code>OnmsAcknowledgable</code>
* ackAction: ack, unack, esc, clear
* ackType: <code>AckType</code. representing either an <code>OnmsAlarm</code>, <code>OnmsNotification</code>, etc.
* refId: The ID of the <code>OnmsAcknowledgable</code>
*
* @param event a {@link org.opennms.netmgt.xml.event.Event} object.
*/
@EventHandler(uei=EventConstants.ACKNOWLEDGE_EVENT_UEI)
public void handleAckEvent(Event event) {
log().info("handleAckEvent: Received acknowledgment event: "+event);
OnmsAcknowledgment ack;
try {
ack = new OnmsAcknowledgment(event);
m_ackService.processAck(ack);
} catch (ParseException e) {
log().error("handleAckEvent: unable to process acknowledgment event: "+event+"\t"+e);
}
}
/**
* <p>handleReloadConfigEvent</p>
*
* @param event a {@link org.opennms.netmgt.xml.event.Event} object.
*/
@EventHandler(uei=EventConstants.RELOAD_DAEMON_CONFIG_UEI)
public void handleReloadConfigEvent(Event event) {
String specifiedDaemon = null;
log().info("handleReloadConfigEvent: processing reload event: "+event+"...");
List<Parm> parms = event.getParmCollection();
for (Parm parm : parms) {
specifiedDaemon = parm.getValue().getContent();
if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName())
&& getName().equalsIgnoreCase(specifiedDaemon)) {
log().debug("handleReloadConfigEvent: reload event is for this daemon: "+getName()+"; reloading configuration...");
try {
m_configDao.reloadConfiguration();
EventBuilder bldr = new EventBuilder(
EventConstants.RELOAD_DAEMON_CONFIG_SUCCESSFUL_UEI,
getName(),
Calendar.getInstance().getTime());
bldr.addParam(EventConstants.PARM_DAEMON_NAME, getName());
m_eventForwarder.sendNow(bldr.getEvent());
log().debug("handleReloadConfigEvent: restarting readers due to reload configuration event...");
this.restartReaders(true);
} catch (Throwable e) {
log().error("handleReloadConfigEvent: "+e, e);
EventBuilder bldr = new EventBuilder(
EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI,
getName(),
Calendar.getInstance().getTime());
bldr.addParam(EventConstants.PARM_DAEMON_NAME, getName());
bldr.addParam(EventConstants.PARM_REASON, e.getLocalizedMessage().substring(0, 128));
m_eventForwarder.sendNow(bldr.getEvent());
}
log().debug("handleReloadConfigEvent: configuration reloaded.");
return; //return here because we are done.
}
}
log().debug("handleReloadConfigEvent: reload event not for this daemon: "+getName()+"; daemon specified is: "+specifiedDaemon);
}
private ThreadCategory log() {
return ThreadCategory.getInstance(getName());
}
/**
* <p>setExecutor</p>
*
* @param executor a {@link java.util.concurrent.ScheduledThreadPoolExecutor} object.
*/
public void setExecutor(ScheduledThreadPoolExecutor executor) {
m_executor = executor;
}
/**
* <p>getExecutor</p>
*
* @return a {@link java.util.concurrent.ScheduledThreadPoolExecutor} object.
*/
public ScheduledThreadPoolExecutor getExecutor() {
return m_executor;
}
/**
* <p>getEventForwarder</p>
*
* @return a {@link org.opennms.netmgt.model.events.EventForwarder} object.
*/
public EventForwarder getEventForwarder() {
return m_eventForwarder;
}
/**
* <p>setEventForwarder</p>
*
* @param eventForwarder a {@link org.opennms.netmgt.model.events.EventForwarder} object.
*/
public void setEventForwarder(EventForwarder eventForwarder) {
m_eventForwarder = eventForwarder;
}
/**
* <p>getAckReaders</p>
*
* @return a {@link java.util.List} object.
*/
protected List<AckReader> getAckReaders() {
return m_ackReaders;
}
//FIXME:this is probably bogus
/**
* <p>setAckReaders</p>
*
* @param ackReaders a {@link java.util.List} object.
*/
public void setAckReaders(List<AckReader> ackReaders) {
m_ackReaders = ackReaders;
}
/**
* <p>getAckService</p>
*
* @return a {@link org.opennms.netmgt.model.acknowledgments.AckService} object.
*/
public AckService getAckService() {
return m_ackService;
}
/**
* <p>setAckService</p>
*
* @param ackService a {@link org.opennms.netmgt.model.acknowledgments.AckService} object.
*/
public void setAckService(AckService ackService) {
m_ackService = ackService;
}
/**
* <p>getConfigDao</p>
*
* @return a {@link org.opennms.netmgt.dao.AckdConfigurationDao} object.
*/
public AckdConfigurationDao getConfigDao() {
return m_configDao;
}
/**
* <p>setConfigDao</p>
*
* @param config a {@link org.opennms.netmgt.dao.AckdConfigurationDao} object.
*/
public void setConfigDao(AckdConfigurationDao config) {
m_configDao = config;
}
/**
* <p>afterPropertiesSet</p>
*
* @throws java.lang.Exception if any.
*/
@Override
public void afterPropertiesSet() throws Exception {
}
/**
* <p>getName</p>
*
* @return a {@link java.lang.String} object.
*/
public String getName() {
return NAME;
}
}