/* * RHQ Management Platform * Copyright (C) 2005-2014 Red Hat, Inc. * All rights reserved. * * This program 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 version 2 of the License. * * This program 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 this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.core.pc.event; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.sigar.SigarProxy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.rhq.core.domain.event.Event; import org.rhq.core.domain.event.EventDefinition; import org.rhq.core.domain.event.EventSource; import org.rhq.core.domain.event.transfer.EventReport; import org.rhq.core.domain.resource.Resource; import org.rhq.core.pc.ContainerService; import org.rhq.core.pc.PluginContainer; import org.rhq.core.pc.PluginContainerConfiguration; import org.rhq.core.pc.util.LoggingThreadFactory; import org.rhq.core.pluginapi.event.EventPoller; import org.rhq.core.system.SigarAccess; /** * Manager for the Plugin Container's Event subsystem. * * @author Ian Springer */ public class EventManager implements ContainerService { private static final Log log = LogFactory.getLog(EventManager.class); private static final String SENDER_THREAD_POOL_NAME = "EventManager.sender"; private static final int SENDER_THREAD_POOL_CORE_SIZE = 2; private static final String POLLER_THREAD_POOL_NAME = "EventManager.poller"; private static final int POLLER_THREAD_POOL_CORE_SIZE = 3; private static final int POLLER_INITIAL_DELAY_SECS = 0; private final PluginContainerConfiguration pcConfig; private final ScheduledThreadPoolExecutor senderThreadPool; private volatile EventReport activeReport; private final ReentrantReadWriteLock reportLock = new ReentrantReadWriteLock(true); private final ScheduledThreadPoolExecutor pollerThreadPool; private final Map<PollerKey, Runnable> pollerThreads; private SigarProxy sigar; public EventManager(PluginContainerConfiguration configuration) { pcConfig = configuration; activeReport = new EventReport(pcConfig.getEventReportMaxPerSource(), pcConfig.getEventReportMaxTotal()); senderThreadPool = new ScheduledThreadPoolExecutor(SENDER_THREAD_POOL_CORE_SIZE, new LoggingThreadFactory( SENDER_THREAD_POOL_NAME, true)); // Set up a thread pool for polling threads. Polling threads will be added to the pool via calls to // registerEventPoller(). pollerThreadPool = new ScheduledThreadPoolExecutor(POLLER_THREAD_POOL_CORE_SIZE, new LoggingThreadFactory( POLLER_THREAD_POOL_NAME, true)); pollerThreads = new HashMap<PollerKey, Runnable>(); } public void initialize() { log.info("Initializing Event Manager..."); // Schedule sender thread(s) to send Event reports to the Server periodically. EventSenderRunner senderRunner = new EventSenderRunner(this); senderThreadPool.scheduleAtFixedRate(senderRunner, pcConfig.getEventSenderInitialDelay(), pcConfig.getEventSenderPeriod(), TimeUnit.SECONDS); log.info("Event Manager initialized."); } @Override public void shutdown() { log.debug("Shutting down event sender thread pool..."); PluginContainer.shutdownExecutorService(this.senderThreadPool, true); log.debug("Shutting down event poller thread pool..."); PluginContainer.shutdownExecutorService(this.pollerThreadPool, true); } void publishEvents(@NotNull Set<Event> events, @NotNull Resource resource) { this.reportLock.readLock().lock(); try { for (Event event : events) { EventSource eventSource = createEventSource(event, resource); this.activeReport.addEvent(event, eventSource); } } catch (Throwable t) { log.error("Failed to add Events for " + resource + " to Event report: " + events, t); } finally { this.reportLock.readLock().unlock(); } } @Nullable SigarProxy getSigar() { if (this.sigar == null) { if (SigarAccess.isSigarAvailable()) { this.sigar = SigarAccess.getSigar(); } } return this.sigar; } /** * Sends the given Event report to the Server, if this Plugin Container has Server services that it can communicate * with. * * @param report the Event report to be sent (this report should be closed from getting any more events added to it) */ void sendEventReport(EventReport report) { if (report.addLimitWarningEvents()) { // add any limit warning events if events were dropped Map<EventSource, Integer> droppedEvents = report.getDroppedEvents(); log.warn("Begin dropped events report"); for (Map.Entry<EventSource, Integer> next : droppedEvents.entrySet()) { log.warn("There were " + next.getValue() + " dropped events for source '" + next.getKey() + "'"); } log.warn("Finish dropped events report"); } if (!report.getEvents().isEmpty() && this.pcConfig.getServerServices() != null) { try { this.pcConfig.getServerServices().getEventServerService().mergeEventReport(report); } catch (Exception e) { log.warn("Failure to report Events to Server.", e); } } } EventReport swapReport() { this.reportLock.writeLock().lock(); try { EventReport previousReport = this.activeReport; this.activeReport = new EventReport(this.pcConfig.getEventReportMaxPerSource(), this.pcConfig .getEventReportMaxTotal()); return previousReport; } finally { this.reportLock.writeLock().unlock(); } } void registerEventPoller(EventPoller poller, int pollingInterval, Resource resource, String sourceLocation) { EventPollerRunner pollerRunner = new EventPollerRunner(poller, resource, this); Runnable pollerFuture = (Runnable) this.pollerThreadPool.scheduleAtFixedRate(pollerRunner, POLLER_INITIAL_DELAY_SECS, pollingInterval, TimeUnit.SECONDS); PollerKey pollerKey = new PollerKey(resource.getId(), poller.getEventType(), sourceLocation); this.pollerThreads.put(pollerKey, pollerFuture); } void unregisterEventPoller(Resource resource, String eventType, String sourceLocation) { if (null == pollerThreadPool || pollerThreadPool.isShutdown()) { return; } PollerKey pollerKey = new PollerKey(resource.getId(), eventType, sourceLocation); if (this.pollerThreads.containsKey(pollerKey)) { Runnable pollerThread = this.pollerThreads.get(pollerKey); boolean wasRemoved = this.pollerThreadPool.remove(pollerThread); if (!wasRemoved) { log.error("Failed to remove poller with " + pollerKey + " from thread pool."); } this.pollerThreads.remove(pollerKey); } } private EventSource createEventSource(Event event, Resource resource) { EventDefinition eventDefinition = EventUtility.getEventDefinition(event.getType(), resource.getResourceType()); if (eventDefinition == null) { throw new IllegalArgumentException("Unknown type - no EventDefinition found with name '" + event.getType() + "'."); } //noinspection ConstantConditions return new EventSource(event.getSourceLocation(), eventDefinition, resource); } static class PollerKey { int resourceId; String eventType; String sourceLocation; PollerKey(int resourceId, String eventType, String sourceLocation) { this.resourceId = resourceId; this.eventType = eventType; this.sourceLocation = sourceLocation; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; PollerKey that = (PollerKey) obj; if (resourceId != that.resourceId) return false; if (!eventType.equals(that.eventType)) return false; if (sourceLocation != null ? !sourceLocation.equals(that.sourceLocation) : that.sourceLocation != null) return false; return true; } @Override public int hashCode() { int result; result = resourceId; result = 31 * result + eventType.hashCode(); result = 31 * result + (sourceLocation != null ? sourceLocation.hashCode() : 0); return result; } @Override public String toString() { return "PollerKey[resourceId=" + this.resourceId + ", eventType=" + this.eventType + "]"; } } }