/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-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.vacuumd; import java.beans.PropertyVetoException; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import java.util.concurrent.ExecutorService; import javax.sql.DataSource; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.ValidationException; import org.opennms.netmgt.EventConstants; import org.opennms.netmgt.config.DataSourceFactory; import org.opennms.netmgt.config.VacuumdConfigFactory; import org.opennms.netmgt.config.vacuumd.Action; import org.opennms.netmgt.config.vacuumd.Automation; import org.opennms.netmgt.config.vacuumd.Statement; import org.opennms.netmgt.config.vacuumd.Trigger; import org.opennms.netmgt.daemon.AbstractServiceDaemon; import org.opennms.netmgt.eventd.EventIpcManager; import org.opennms.netmgt.model.events.EventBuilder; import org.opennms.netmgt.model.events.EventListener; import org.opennms.netmgt.scheduler.LegacyScheduler; import org.opennms.netmgt.scheduler.Schedule; import org.opennms.netmgt.scheduler.Scheduler; import org.opennms.netmgt.xml.event.Event; import org.opennms.netmgt.xml.event.Parm; /** * Implements a daemon whose job it is to run periodic updates against the * database for database maintenance work. * * @author <a href=mailto:brozow@opennms.org>Mathew Brozowski</a> * @author <a href=mailto:david@opennms.org>David Hustace</a> * @author <a href=mailto:dj@opennms.org>DJ Gregor</a> */ public class Vacuumd extends AbstractServiceDaemon implements Runnable, EventListener { private static volatile Vacuumd m_singleton; private volatile Thread m_thread; private volatile long m_startTime; private volatile boolean m_stopped = false; private volatile LegacyScheduler m_scheduler; private volatile EventIpcManager m_eventMgr; /** * <p>getSingleton</p> * * @return a {@link org.opennms.netmgt.vacuumd.Vacuumd} object. */ public synchronized static Vacuumd getSingleton() { if (m_singleton == null) { m_singleton = new Vacuumd(); } return m_singleton; } /** * <p>Constructor for Vacuumd.</p> */ public Vacuumd() { super("OpenNMS.Vacuumd"); } /* * (non-Javadoc) * * @see org.opennms.netmgt.vacuumd.jmx.VacuumdMBean#init() */ /** {@inheritDoc} */ @Override protected void onInit() { try { log().info("Loading the configuration file."); VacuumdConfigFactory.init(); getEventManager().addEventListener(this, EventConstants.RELOAD_VACUUMD_CONFIG_UEI); initializeDataSources(); } catch (Throwable ex) { log().error("Failed to load outage configuration", ex); throw new UndeclaredThrowableException(ex); } log().info("Vacuumd initialization complete"); createScheduler(); scheduleAutomations(); } private void initializeDataSources() throws MarshalException, ValidationException, IOException, ClassNotFoundException, PropertyVetoException, SQLException { for (Trigger trigger : getVacuumdConfig().getTriggers()) { DataSourceFactory.init(trigger.getDataSource()); } for (Action action : getVacuumdConfig().getActions()) { DataSourceFactory.init(action.getDataSource()); } } /** {@inheritDoc} */ @Override protected void onStart() { m_startTime = System.currentTimeMillis(); m_thread = new Thread(this, "Vacuumd-Thread"); m_thread.start(); m_scheduler.start(); } /** {@inheritDoc} */ @Override protected void onStop() { m_stopped = true; m_scheduler.stop(); } /** {@inheritDoc} */ @Override protected void onPause() { m_scheduler.pause(); m_stopped = true; } /** {@inheritDoc} */ @Override protected void onResume() { m_thread = new Thread(this, "Vacuumd-Thread"); m_thread.start(); m_scheduler.resume(); } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ /** * <p>run</p> */ public void run() { log().info("Vacuumd scheduling started"); long now = System.currentTimeMillis(); long period = getVacuumdConfig().getPeriod(); log().info("Vacuumd sleeping until time to execute statements period = " + period); long waitTime = 500L; while (!m_stopped) { try { now = waitPeriod(now, period, waitTime); log().info("Vacuumd beginning to execute statements"); executeStatements(); m_startTime = System.currentTimeMillis(); } catch (Throwable e) { log().error("Unexpected exception: ", e); } } } /** * <p>executeStatements</p> */ protected void executeStatements() { if (!m_stopped) { List<Statement> statements = getVacuumdConfig().getStatements(); for (Statement statement : statements) { runUpdate(statement.getContent(), statement.getTransactional()); } } } /** * @param now * @param period * @param waitTime * @return */ private long waitPeriod(long now, long period, long waitTime) { int count = 0; while (!m_stopped && ((now - m_startTime) < period)) { try { if (count % 100 == 0) { log().debug("Vacuumd: " + (period - now + m_startTime) + "ms remaining to execution."); } Thread.sleep(waitTime); now = System.currentTimeMillis(); count++; } catch (InterruptedException e) { // FIXME: what do I do here? } } return now; } private void runUpdate(String sql, boolean transactional) { log().info("Vacuumd executing statement: " + sql); // update the database Connection dbConn = null; //initially set doCommit to avoid doing a commit in the finally //if an exception is thrown. boolean commitRequired = false; boolean autoCommitFlag = !transactional; try { dbConn = getDataSourceFactory().getConnection(); dbConn.setAutoCommit(autoCommitFlag); PreparedStatement stmt = dbConn.prepareStatement(sql); int count = stmt.executeUpdate(); stmt.close(); if (log().isDebugEnabled()) { log().debug("Vacuumd: Ran update " + sql + ": this affected " + count + " rows"); } commitRequired = transactional; } catch (SQLException ex) { log().error("Vacuumd: Database error execuating statement " + sql, ex); } finally { if (dbConn != null) { try { if (commitRequired) { dbConn.commit(); } else if (transactional) { dbConn.rollback(); } } catch (SQLException ex) { } finally { if (dbConn != null) { try { dbConn.close(); } catch (Throwable e) { } } } } } } private void createScheduler() { try { log().debug("init: Creating Vacuumd scheduler"); m_scheduler = new LegacyScheduler("Vacuumd", 2); } catch (RuntimeException e) { log().fatal("init: Failed to create Vacuumd scheduler: " + e, e); throw e; } } /** * <p>getScheduler</p> * * @return a {@link org.opennms.netmgt.scheduler.Scheduler} object. */ public Scheduler getScheduler() { return m_scheduler; } private void scheduleAutomations() { for (Automation auto : getVacuumdConfig().getAutomations()) { scheduleAutomation(auto); } } private void scheduleAutomation(Automation auto) { if (auto.getActive()) { AutomationProcessor ap = new AutomationProcessor(auto); Schedule s = new Schedule(ap, new AutomationInterval(auto.getInterval()), m_scheduler); ap.setSchedule(s); s.schedule(); } } /** * <p>getEventManager</p> * * @return a {@link org.opennms.netmgt.eventd.EventIpcManager} object. */ public EventIpcManager getEventManager() { return m_eventMgr; } /** * <p>setEventManager</p> * * @param eventMgr a {@link org.opennms.netmgt.eventd.EventIpcManager} object. */ public void setEventManager(EventIpcManager eventMgr) { m_eventMgr = eventMgr; } /** {@inheritDoc} */ public void onEvent(Event event) { if (isReloadConfigEvent(event)) { handleReloadConifgEvent(); } } private void handleReloadConifgEvent() { log().info("onEvent: reloading configuration..."); EventBuilder ebldr = null; try { log().debug("onEvent: Number of elements in schedule:"+m_scheduler.getScheduled()+"; calling stop on scheduler..."); stop(); ExecutorService runner = m_scheduler.getRunner(); while (!runner.isShutdown() || m_scheduler.getStatus() != STOPPED) { log().debug("onEvent: waiting for scheduler to stop." + " Current status of scheduler: "+m_scheduler.getStatus()+"; Current status of runner: "+(runner.isTerminated() ? "TERMINATED" : (runner.isShutdown() ? "SHUTDOWN" : "RUNNING"))); Thread.sleep(500); } log().debug("onEvent: Current status of scheduler: "+m_scheduler.getStatus()+"; Current status of runner: "+(runner.isTerminated() ? "TERMINATED" : (runner.isShutdown() ? "SHUTDOWN" : "RUNNING"))); log().debug("onEvent: Number of elements in schedule:"+m_scheduler.getScheduled()); log().debug("onEvent: reloading vacuumd configuration."); VacuumdConfigFactory.reload(); log().debug("onEvent: creating new schedule and rescheduling automations."); init(); log().debug("onEvent: restarting vacuumd and scheduler."); start(); log().debug("onEvent: Number of elements in schedule:"+m_scheduler.getScheduled()); ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_SUCCESSFUL_UEI, getName()); ebldr.addParam(EventConstants.PARM_DAEMON_NAME, "Vacuumd"); } catch (MarshalException e) { log().error("onEvent: problem marshaling vacuumd configuration: " + e, e); ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, getName()); ebldr.addParam(EventConstants.PARM_DAEMON_NAME, "Vacuumd"); ebldr.addParam(EventConstants.PARM_REASON, e.getLocalizedMessage().substring(0, 128)); } catch (ValidationException e) { log().error("onEvent: problem validating vacuumd configuration: " + e, e); ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, getName()); ebldr.addParam(EventConstants.PARM_DAEMON_NAME, "Vacuumd"); ebldr.addParam(EventConstants.PARM_REASON, e.getLocalizedMessage().substring(0, 128)); } catch (IOException e) { log().error("onEvent: IO problem reading vacuumd configuration: " + e, e); ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, getName()); ebldr.addParam(EventConstants.PARM_DAEMON_NAME, "Vacuumd"); ebldr.addParam(EventConstants.PARM_REASON, e.getLocalizedMessage().substring(0, 128)); } catch (InterruptedException e) { log().error("onEvent: Problem interrupting current Vacuumd Thread: " + e, e); ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, getName()); ebldr.addParam(EventConstants.PARM_DAEMON_NAME, "Vacuumd"); ebldr.addParam(EventConstants.PARM_REASON, e.getLocalizedMessage().substring(0, 128)); } log().info("onEvent: completed configuration reload."); if (ebldr != null) { m_eventMgr.sendNow(ebldr.getEvent()); } } private boolean isReloadConfigEvent(Event event) { boolean isTarget = false; if (EventConstants.RELOAD_DAEMON_CONFIG_UEI.equals(event.getUei())) { List<Parm> parmCollection = event.getParmCollection(); for (Parm parm : parmCollection) { if (EventConstants.PARM_DAEMON_NAME.equals(parm.getParmName()) && "Vacuumd".equalsIgnoreCase(parm.getValue().getContent())) { isTarget = true; break; } } //Depreciating this one... } else if (EventConstants.RELOAD_VACUUMD_CONFIG_UEI.equals(event.getUei())) { isTarget = true; } return isTarget; } private VacuumdConfigFactory getVacuumdConfig() { return VacuumdConfigFactory.getInstance(); } private DataSource getDataSourceFactory() { return DataSourceFactory.getInstance(); } }