/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2010-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.readers; import java.util.concurrent.Future; import java.util.concurrent.ScheduledThreadPoolExecutor; import org.opennms.core.utils.BeanUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.ackd.AckReader; import org.opennms.netmgt.dao.AckdConfigurationDao; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Assert; /** * Acknowledgment Reader implementation using Java Mail * * DONE: Identify acknowledgments for sent notifications * DONE: Identify acknowledgments for alarm IDs (how the send knows the ID, good question) * DONE: Persist acknowledgments * DONE: Identify escalation reply * DONE: Identify clear reply * DOND: Identify unacknowledged reply * DONE: Formalize Acknowledgment parameters (ack-type, id) * DONE: JavaMail configuration factory * DONE: Ackd configuration factory * TODO: Associate email replies with openNMS user * DONE: Finish scheduling component of JavaAckReader * DONE: Configurable Schedule * DONE: Identify Java Mail configuration element to use for reading replies * TODO: Migrate JavaMailNotificationStrategy to new JavaMail Configuration and JavaSendMailer * TODO: Migrate Availability Reports send via JavaMail to new JavaMail Configuration and JavaSendMailer * TODO: Move reading email messages from MTM to JavaReadMailer class * DONE: Need an event to cause re-loading of schedules based on changes to ackd-configuration * DONE: Do some proper logging * DONE: Handle "enabled" flag of the readers in ackd-configuration * DONE: Move executor to Ackd daemon * * @author <a href=mailto:david@opennms.org>David Hustace</a> * @version $Id: $ */ public class DefaultAckReader implements AckReader, InitializingBean { private volatile String m_name; private volatile Future<?> m_future; private AckProcessor m_ackProcessor; private ReaderSchedule m_schedule; private volatile AckReaderState m_state = AckReaderState.STOPPED; @Autowired private volatile AckdConfigurationDao m_ackdConfigDao; /** * <p>afterPropertiesSet</p> * * @throws java.lang.Exception if any. */ @Override public void afterPropertiesSet() throws Exception { BeanUtils.assertAutowiring(this); boolean state = (m_ackProcessor != null); Assert.state(state, "Dependency injection failed; one or more fields are null."); } private synchronized void start(final ScheduledThreadPoolExecutor executor) { if (m_schedule == null) { m_schedule = ReaderSchedule.createSchedule(); } this.start(executor, m_schedule, true); } /** {@inheritDoc} */ public synchronized void start(final ScheduledThreadPoolExecutor executor, final ReaderSchedule schedule, boolean reloadConfig) throws IllegalStateException { if (reloadConfig) { //FIXME:The reload of JavaMailConfiguration is made here because the DAO is there. Perhaps that should be changed. log().info("start: reloading ack processor configuration..."); m_ackProcessor.reloadConfigs(); log().info("start: ack processor configuration reloaded."); } if (AckReaderState.STOPPED.equals(getState())) { this.setState(AckReaderState.START_PENDING); this.setSchedule(executor, schedule, false); log().info("start: Starting reader..."); this.scheduleReads(executor); this.setState(AckReaderState.STARTED); log().info("start: Reader started."); } else { IllegalStateException e = new IllegalStateException("Reader is not in a stopped state. Reader state is: "+getState()); log().error("start: "+e, e); throw e; } } /** * <p>pause</p> * * @throws java.lang.IllegalStateException if any. */ public synchronized void pause() throws IllegalStateException { if (AckReaderState.STARTED.equals(getState()) || AckReaderState.RESUMED.equals(getState())) { log().info("pause: lock acquired; pausing reader..."); setState(AckReaderState.PAUSE_PENDING); if (m_future != null) { m_future.cancel(false); m_future = null; } setState(AckReaderState.PAUSED); log().info("pause: Reader paused."); } else { IllegalStateException e = new IllegalStateException("Reader is not in a running state (STARTED or RESUMED). Reader state is: "+getState()); log().error("pause: "+e, e); throw e; } } /** {@inheritDoc} */ public synchronized void resume(final ScheduledThreadPoolExecutor executor) throws IllegalStateException { if (AckReaderState.PAUSED.equals(getState())) { setState(AckReaderState.RESUME_PENDING); log().info("resume: lock acquired; resuming reader..."); scheduleReads(executor); setState(AckReaderState.RESUMED); log().info("resume: reader resumed."); } else { IllegalStateException e = new IllegalStateException("Reader is not in a paused state, cannot resume. Reader state is: "+getState()); log().error("resume: "+e, e); throw e; } } /** * <p>stop</p> * * @throws java.lang.IllegalStateException if any. */ public synchronized void stop() throws IllegalStateException { if (!AckReaderState.STOPPED.equals(getState())) { setState(AckReaderState.STOP_PENDING); log().info("stop: lock acquired; stopping reader..."); if (m_future != null) { m_future.cancel(false); m_future = null; } setState(AckReaderState.STOPPED); log().info("stop: Reader stopped."); } else { IllegalStateException e = new IllegalStateException("Reader is already stopped."); log().error("stop: "+e, e); throw e; } } private synchronized void scheduleReads(final ScheduledThreadPoolExecutor executor) { log().debug("scheduleReads: acquired lock, creating schedule..."); executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); m_future = executor.scheduleWithFixedDelay(this.getAckProcessor(), getSchedule().getInitialDelay(), getSchedule().getInterval(), getSchedule().getUnit()); log().debug("scheduleReads: exited lock, schedule updated."); log().debug("scheduleReads: schedule is:" + " attempts remaining: "+getSchedule().getAttemptsRemaining()+ "; initial delay: "+getSchedule().getInitialDelay()+ "; interval: "+getSchedule().getInterval()+ "; unit: "+getSchedule().getUnit()); log().debug("scheduleReads: executor details:"+ " active count: "+executor.getActiveCount()+ "; completed task count: "+executor.getCompletedTaskCount()+ "; task count: "+executor.getTaskCount()+ "; queue size: "+executor.getQueue().size()); } private ThreadCategory log() { return ThreadCategory.getInstance(this.getClass()); } /** {@inheritDoc} */ @Override public String toString() { return getClass().getCanonicalName(); } /** {@inheritDoc} */ public void setAckProcessor(AckProcessor ackProcessor) { m_ackProcessor = ackProcessor; } /** * <p>getAckProcessor</p> * * @return a {@link org.opennms.netmgt.ackd.readers.AckProcessor} object. */ public AckProcessor getAckProcessor() { return m_ackProcessor; } /** * <p>setAckdConfigDao</p> * * @param ackdConfigDao a {@link org.opennms.netmgt.dao.AckdConfigurationDao} object. */ public void setAckdConfigDao(AckdConfigurationDao ackdConfigDao) { m_ackdConfigDao = ackdConfigDao; } /** * <p>getAckdConfigDao</p> * * @return a {@link org.opennms.netmgt.dao.AckdConfigurationDao} object. */ public AckdConfigurationDao getAckdConfigDao() { return m_ackdConfigDao; } /** * Gets a new schedule and optionally reschedules <code>MailAckProcessor</code> * @param schedule * @param reschedule */ private synchronized void setSchedule(final ScheduledThreadPoolExecutor executor, ReaderSchedule schedule, boolean reschedule) { m_schedule = schedule; if (reschedule) { stop(); start(executor); } } private ReaderSchedule getSchedule() { if (m_schedule == null) { m_schedule = ReaderSchedule.createSchedule(); } return m_schedule; } /** * Anything calling this method should already have the lock. * * @param state */ private synchronized void setState(AckReaderState state) { m_state = state; } /** * <p>getState</p> * * @return a AckReaderState object. */ public AckReaderState getState() { return m_state; } /** * <p>getFuture</p> * * @return a {@link java.util.concurrent.Future} object. */ public Future<?> getFuture() { return m_future; } /** * <p>getName</p> * * @return a {@link java.lang.String} object. */ public String getName() { return m_name; } /** {@inheritDoc} */ public synchronized void setName(String name) { m_name = name; } }