/******************************************************************************* * 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.poller.remote.support; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.net.InetAddress; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.LogUtils; import org.opennms.netmgt.model.OnmsLocationMonitor.MonitorStatus; import org.opennms.netmgt.model.OnmsMonitoringLocationDefinition; import org.opennms.netmgt.model.PollStatus; import org.opennms.netmgt.poller.DistributionContext; import org.opennms.netmgt.poller.remote.ConfigurationChangedListener; import org.opennms.netmgt.poller.remote.PollService; import org.opennms.netmgt.poller.remote.PolledService; import org.opennms.netmgt.poller.remote.PollerBackEnd; import org.opennms.netmgt.poller.remote.PollerConfiguration; import org.opennms.netmgt.poller.remote.PollerFrontEnd; import org.opennms.netmgt.poller.remote.PollerSettings; import org.opennms.netmgt.poller.remote.ServicePollState; import org.opennms.netmgt.poller.remote.ServicePollStateChangedEvent; import org.opennms.netmgt.poller.remote.ServicePollStateChangedListener; import org.opennms.netmgt.poller.remote.TimeAdjustment; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * <p>DefaultPollerFrontEnd class.</p> * * @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a> * @version $Id: $ */ public class DefaultPollerFrontEnd implements PollerFrontEnd, InitializingBean, DisposableBean { private class Disconnected extends RunningState { @Override public boolean isDisconnected() { return true; } @Override public void stop() { // don't call do stop as we are disconnected from the server setState(new Registering()); } @Override protected void onConfigChanged() { doLoadConfig(); setState(new Running()); } @Override protected void onPaused() { doPause(); setState(new Paused()); } @Override protected void onStarted() { doLoadConfig(); setState(new Running()); } } public class Initial extends State { @Override public void initialize() { try { final Integer monitorId = doInitialize(); if (monitorId == null) { setState(new Registering()); } else if (doPollerStart()) { setState(new Running()); } else { // the poller has been deleted doDelete(); setState(new Registering()); } } catch (final RuntimeException e) { setState(new FatalExceptionOccurred()); // rethrow the exception on initialize so we exit if we fail to initialize throw e; } } @Override public boolean isRegistered() { return false; } } private class Paused extends RunningState { @Override protected void onConfigChanged() { doLoadConfig(); } @Override public boolean isPaused() { return true; } @Override protected void onDisconnected() { doDisconnected(); setState(new Disconnected()); } @Override protected void onStarted() { doResume(); setState(new Running()); } } private class Registering extends State { @Override public boolean isRegistered() { return false; } @Override public void register(final String location) { try { doRegister(location); setState(new Running()); } catch (final Exception e) { LogUtils.warnf(this, e, "Unable to register."); setState(new Disconnected()); } } } private class RunningState extends State { @Override public void pollService(final Integer serviceId) { /* most running states do nothing here */ } @Override public void checkIn() { try { final MonitorStatus status = doCheckIn(); switch (status) { case CONFIG_CHANGED: onConfigChanged(); break; case DELETED: onDeleted(); break; case DISCONNECTED: onDisconnected(); break; case PAUSED: onPaused(); break; case STARTED: onStarted(); break; } } catch (final Exception e) { LogUtils.errorf(this, e, "Unexpected exception occurred while checking in."); setState(new FatalExceptionOccurred()); } final String killSwitchFileName = System.getProperty("opennms.poller.killSwitch.resource"); if (!"".equals(killSwitchFileName) && killSwitchFileName != null) { final File killSwitch = new File(System.getProperty("opennms.poller.killSwitch.resource")); if (!killSwitch.exists()) { LogUtils.infof(this, "Kill-switch file %s does not exist; stopping.", killSwitch.getPath()); doStop(); } } } @Override public boolean isStarted() { return true; } @Override public void stop() { try { doStop(); setState(new Registering()); } catch (final Exception e) { LogUtils.errorf(this, e, "Unexpected exception occurred while stopping."); setState(new FatalExceptionOccurred()); } } protected void onConfigChanged() { /* do nothing be default */ } protected void onDeleted() { doDelete(); setState(new Registering()); } protected void onDisconnected() { /* do nothing be default */ } protected void onPaused() { /* do nothing be default */ } protected void onStarted() { /* do nothing be default */ } } public class Running extends RunningState { @Override public void pollService(final Integer polledServiceId) { try { doPollService(polledServiceId); } catch (Throwable e) { LogUtils.errorf(this, e, "Unexpected exception occurred while polling service ID %s.", polledServiceId); setState(new FatalExceptionOccurred()); } } @Override protected void onConfigChanged() { doLoadConfig(); } @Override protected void onDisconnected() { doDisconnected(); setState(new Disconnected()); } @Override protected void onPaused() { doPause(); setState(new Paused()); } } public class FatalExceptionOccurred extends State { @Override public boolean isExitNecessary() { return true; } } private abstract class State { public void checkIn() { // a pollerCheckingIn in any state that doesn't respond just does nothing } public IllegalStateException illegalState(final String msg) { return new IllegalStateException(msg + " State: " + this); } public void initialize() { throw illegalState("Initialize called on invalid state."); } public boolean isInitialized() { return true; } public boolean isRegistered() { return true; } public boolean isStarted() { return false; } public boolean isPaused() { return false; } public boolean isDisconnected() { return false; } public boolean isExitNecessary() { return false; } public void pollService(final Integer serviceId) { throw illegalState("Cannot poll from this state."); } public void register(final String location) { throw illegalState("Cannot register from this state."); } public void stop() { // do nothing here by default as the actual exit is managed by the external program } public String toString() { return getClass().getSimpleName(); } } private State m_state = new Initial(); // injected dependencies private PollerBackEnd m_backEnd; private PollerSettings m_pollerSettings; private PollService m_pollService; private TimeAdjustment m_timeAdjustment; // listeners private LinkedList<PropertyChangeListener> m_propertyChangeListeners = new LinkedList<PropertyChangeListener>(); private LinkedList<ServicePollStateChangedListener> m_servicePollStateChangedListeners = new LinkedList<ServicePollStateChangedListener>(); private LinkedList<ConfigurationChangedListener> m_configChangeListeners = new LinkedList<ConfigurationChangedListener>(); // current configuration private PollerConfiguration m_pollerConfiguration; // current state of polled services private Map<Integer, ServicePollState> m_pollState = new LinkedHashMap<Integer, ServicePollState>(); /** {@inheritDoc} */ public void addConfigurationChangedListener(ConfigurationChangedListener l) { m_configChangeListeners.addFirst(l); } /** * <p>doResume</p> */ public void doResume() { doLoadConfig(); } /** * <p>doPause</p> */ public void doPause() { // do I need to do anything here? } /** * <p>doDisconnected</p> */ public void doDisconnected() { doLoadConfig(); } /** {@inheritDoc} */ public void addPropertyChangeListener(final PropertyChangeListener listener) { m_propertyChangeListeners.addFirst(listener); } /** {@inheritDoc} */ public void addServicePollStateChangedListener(final ServicePollStateChangedListener listener) { m_servicePollStateChangedListeners.addFirst(listener); } /** * <p>afterPropertiesSet</p> * * @throws java.lang.Exception if any. */ @Override public void afterPropertiesSet() throws Exception { assertNotNull(m_timeAdjustment, "timeAdjustment"); m_state.initialize(); } /** * <p>checkConfig</p> */ public void checkConfig() { m_state.checkIn(); } /** * <p>destroy</p> * * @throws java.lang.Exception if any. */ public void destroy() throws Exception { stop(); } /** * <p>doCheckIn</p> * * @return a {@link org.opennms.netmgt.model.OnmsLocationMonitor.MonitorStatus} object. */ public MonitorStatus doCheckIn() { return m_backEnd.pollerCheckingIn(getMonitorId(), getCurrentConfigTimestamp()); } /** * <p>doDelete</p> */ public void doDelete() { setMonitorId(null); } /** * <p>doInitialize</p> * * @return a {@link java.lang.Integer} object. */ public Integer doInitialize() { assertNotNull(m_backEnd, "pollerBackEnd"); assertNotNull(m_pollService, "pollService"); assertNotNull(m_pollerSettings, "pollerSettings"); return getMonitorId(); } /** * <p>doPollerStart</p> * * @return a boolean. */ public boolean doPollerStart() { if (!m_backEnd.pollerStarting(getMonitorId(), getDetails())) { // the monitor has been deleted on the server return false; } doLoadConfig(); return true; } /** * <p>doPollService</p> * * @param polledServiceId a {@link java.lang.Integer} object. */ public void doPollService(final Integer polledServiceId) { final PollStatus result = doPoll(polledServiceId); if (result == null) return; updateServicePollState(polledServiceId, result); m_backEnd.reportResult(getMonitorId(), polledServiceId, result); } /** * <p>doRegister</p> * * @param location a {@link java.lang.String} object. */ public void doRegister(final String location) { int monitorId = m_backEnd.registerLocationMonitor(location); setMonitorId(monitorId); doPollerStart(); } /** * <p>doStop</p> */ public void doStop() { m_backEnd.pollerStopping(getMonitorId()); } /** * <p>getDetails</p> * * @return a {@link java.util.Map} object. */ public Map<String, String> getDetails() { final HashMap<String, String> details = new HashMap<String, String>(); final Properties p = System.getProperties(); for (final Map.Entry<Object, Object> e : p.entrySet()) { if (e.getKey().toString().startsWith("os.") && e.getValue() != null) { details.put(e.getKey().toString(), e.getValue().toString()); } } final InetAddress us = InetAddressUtils.getLocalHostAddress(); details.put("org.opennms.netmgt.poller.remote.hostAddress", InetAddressUtils.str(us)); details.put("org.opennms.netmgt.poller.remote.hostName", us.getHostName()); return Collections.unmodifiableMap(details); } /** * <p>getMonitorId</p> * * @return a {@link java.lang.Integer} object. */ public Integer getMonitorId() { return m_pollerSettings.getMonitorId(); } /** * <p>getMonitoringLocations</p> * * @return a {@link java.util.Collection} object. */ public Collection<OnmsMonitoringLocationDefinition> getMonitoringLocations() { assertInitialized(); return m_backEnd.getMonitoringLocations(); } /** * <p>getMonitorName</p> * * @return a {@link java.lang.String} object. */ public String getMonitorName() { return (isRegistered() ? m_backEnd.getMonitorName(getMonitorId()) : ""); } /** * <p>getPolledServices</p> * * @return a {@link java.util.Collection} object. */ public Collection<PolledService> getPolledServices() { return Arrays.asList(m_pollerConfiguration.getPolledServices()); } /** * <p>getPollerPollState</p> * * @return a {@link java.util.List} object. */ public List<ServicePollState> getPollerPollState() { synchronized (m_pollState) { return new LinkedList<ServicePollState>(m_pollState.values()); } } /** {@inheritDoc} */ public ServicePollState getServicePollState(int polledServiceId) { synchronized (m_pollState) { return m_pollState.get(polledServiceId); } } /** * <p>getStatus</p> * * @return a {@link java.lang.String} object. */ public String getStatus() { return m_state.toString(); } /** * <p>isRegistered</p> * * @return a boolean. */ public boolean isRegistered() { return m_state.isRegistered(); } /** * <p>isStarted</p> * * @return a boolean. */ public boolean isStarted() { return m_state.isStarted(); } /** {@inheritDoc} */ public void pollService(final Integer polledServiceId) { m_state.pollService(polledServiceId); } /** {@inheritDoc} */ public void register(final String monitoringLocation) { m_state.register(monitoringLocation); } /** {@inheritDoc} */ public void removeConfigurationChangedListener(final ConfigurationChangedListener listener) { m_configChangeListeners.remove(listener); } /** {@inheritDoc} */ public void removePropertyChangeListener(final PropertyChangeListener listener) { m_propertyChangeListeners.remove(listener); } /** {@inheritDoc} */ public void removeServicePollStateChangedListener(final ServicePollStateChangedListener listener) { m_servicePollStateChangedListeners.remove(listener); } /** {@inheritDoc} */ public void setInitialPollTime(final Integer polledServiceId, final Date initialPollTime) { final ServicePollState pollState = getServicePollState(polledServiceId); if (pollState == null) return; pollState.setInitialPollTime(initialPollTime); fireServicePollStateChanged(pollState.getPolledService(), pollState.getIndex()); } /** * <p>setMonitorId</p> * * @param monitorId a {@link java.lang.Integer} object. */ public void setMonitorId(final Integer monitorId) { m_pollerSettings.setMonitorId(monitorId); } /** * <p>setPollerBackEnd</p> * * @param backEnd a {@link org.opennms.netmgt.poller.remote.PollerBackEnd} object. */ public void setPollerBackEnd(final PollerBackEnd backEnd) { m_backEnd = backEnd; } /** * <p>setPollerSettings</p> * * @param settings a {@link org.opennms.netmgt.poller.remote.PollerSettings} object. */ public void setPollerSettings(final PollerSettings settings) { m_pollerSettings = settings; } /** * @param timeAdjustment the timeAdjustment to set */ public void setTimeAdjustment(TimeAdjustment timeAdjustment) { m_timeAdjustment = timeAdjustment; } /** * <p>setPollService</p> * * @param pollService a {@link org.opennms.netmgt.poller.remote.PollService} object. */ public void setPollService(final PollService pollService) { m_pollService = pollService; } /** * <p>stop</p> */ public void stop() { m_state.stop(); } private void assertInitialized() { Assert.isTrue(isInitialized(), "afterProperties set has not been called"); } private void assertNotNull(final Object propertyValue, final String propertyName) { Assert.state(propertyValue != null, propertyName + " must be set for instances of " + getClass()); } @SuppressWarnings("unused") private void assertRegistered() { Assert.state(isRegistered(), "The poller must be registered before we can poll or get its configuration"); } private void doLoadConfig() { Date oldTime = getCurrentConfigTimestamp(); try { m_pollService.setServiceMonitorLocators(m_backEnd.getServiceMonitorLocators(DistributionContext.REMOTE_MONITOR)); m_pollerConfiguration = retrieveLatestConfiguration(); synchronized (m_pollState) { int i = 0; m_pollState.clear(); for (final PolledService service : getPolledServices()) { m_pollService.initialize(service); m_pollState.put(service.getServiceId(), new ServicePollState(service, i++)); } } fireConfigurationChange(oldTime, getCurrentConfigTimestamp()); } catch (final Exception e) { LogUtils.warnf(this, e, "Unable to get updated poller configuration."); if (m_pollerConfiguration == null) { m_pollerConfiguration = new EmptyPollerConfiguration(); } } } private PollerConfiguration retrieveLatestConfiguration() { PollerConfiguration config = m_backEnd.getPollerConfiguration(getMonitorId()); m_timeAdjustment.setMasterTime(config.getServerTime()); return config; } private PollStatus doPoll(final Integer polledServiceId) { final PolledService polledService = getPolledService(polledServiceId); if (polledService == null) return null; final PollStatus result = m_pollService.poll(polledService); return result; } private void fireConfigurationChange(final Date oldTime, final Date newTime) { final PropertyChangeEvent e = new PropertyChangeEvent(this, "configuration", oldTime, newTime); for (final ConfigurationChangedListener listener : m_configChangeListeners) { listener.configurationChanged(e); } } private void firePropertyChange(final String propertyName, final Object oldValue, final Object newValue) { if (nullSafeEquals(oldValue, newValue)) { // no change no event return; } final PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); for (final PropertyChangeListener listener : m_propertyChangeListeners) { listener.propertyChange(event); } } private boolean nullSafeEquals(final Object oldValue, final Object newValue) { return (oldValue == newValue ? true : ObjectUtils.nullSafeEquals(oldValue, newValue)); } private void fireServicePollStateChanged(final PolledService polledService, final int index) { final ServicePollStateChangedEvent event = new ServicePollStateChangedEvent(polledService, index); for (final ServicePollStateChangedListener listener : m_servicePollStateChangedListeners) { listener.pollStateChange(event); } } private Date getCurrentConfigTimestamp() { return (m_pollerConfiguration == null ? null : m_pollerConfiguration.getConfigurationTimestamp()); } private PolledService getPolledService(final Integer polledServiceId) { final ServicePollState servicePollState = getServicePollState(polledServiceId); return (servicePollState == null ? null : servicePollState.getPolledService()); } private boolean isInitialized() { return m_state.isInitialized(); } private void setState(final State newState) { final boolean started = isStarted(); final boolean registered = isRegistered(); final boolean paused = isPaused(); final boolean disconnected = isDisconnected(); final boolean exitNecessary = isExitNecessary(); m_state = newState; firePropertyChange("exitNecessary", exitNecessary, isExitNecessary()); firePropertyChange("started", started, isStarted()); firePropertyChange("registered", registered, isRegistered()); firePropertyChange("paused", paused, isPaused()); firePropertyChange("disconnected", disconnected, isDisconnected()); } private boolean isDisconnected() { return m_state.isDisconnected(); } private boolean isPaused() { return m_state.isPaused(); } /** * <p>isExitNecessary</p> * * @return a boolean. */ public boolean isExitNecessary() { return m_state.isExitNecessary(); } private void updateServicePollState(final Integer polledServiceId, final PollStatus result) { final ServicePollState pollState = getServicePollState(polledServiceId); if (pollState == null) return; pollState.setLastPoll(result); fireServicePollStateChanged(pollState.getPolledService(), pollState.getIndex()); } }