modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/EventContext.java | 7 modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventPoller.java | 64 ++-- modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventResourceComponentHelper.java | 160 ++++++---- modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventContextImpl.java | 3 modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventManager.java | 7 5 files changed, 156 insertions(+), 85 deletions(-)
New commits: commit 5b7557b941711586d3c3be1447e0a9c3319fe029 Author: Ian Springer ian.springer@redhat.com Date: Tue Jan 31 12:21:04 2012 -0500
[BZ 785026] log an informative warning, if log files cannot be polled for events due to SIGAR being disabled or unavailable (https://bugzilla.redhat.com/show_bug.cgi?id=785026)
diff --git a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/EventContext.java b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/EventContext.java index f9bb654..8651272 100644 --- a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/EventContext.java +++ b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/EventContext.java @@ -22,6 +22,7 @@ */ package org.rhq.core.pluginapi.event;
+import com.sun.istack.Nullable; import org.hyperic.sigar.SigarProxy; import org.jetbrains.annotations.NotNull;
@@ -90,10 +91,10 @@ public interface EventContext {
/** * Gets an instance of Sigar. Plugins that need to use Sigar, should use this method to get an instance, rather than - * instantiating Sigar themselves. + * instantiating Sigar themselves. Returns null if the native layer is unavailable or has been disabled. * - * @return an instance of Sigar + * @return an instance of Sigar, or null if the native layer is unavailable or has been disabled */ - @NotNull + @Nullable SigarProxy getSigar(); } diff --git a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventPoller.java b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventPoller.java index 6fcb6d1..b4e19c4 100644 --- a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventPoller.java +++ b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventPoller.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2008 Red Hat, Inc. + * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -54,6 +54,7 @@ public class LogFileEventPoller implements EventPoller { private FileInfo logFileInfo; private LogEntryProcessor entryProcessor; private EventContext eventContext; + private boolean initialized;
public LogFileEventPoller(EventContext eventContext, String eventType, File logFile, LogEntryProcessor entryProcessor) { @@ -73,45 +74,54 @@ public class LogFileEventPoller implements EventPoller { return this.logFile.getPath(); }
- // we can't get the FileInfo in the constructor because pollers are constructed during pc initialization, and - // at that time the eventManager is not available (and so we can't get sigar). - private FileInfo getFileInfo() { - if (null == this.logFileInfo) { - try { - SigarProxy sigar = eventContext.getSigar(); - this.logFileInfo = new LogFileInfo(sigar.getFileInfo(logFile.getPath())); - // once we have the file info we can let go of the event context, just in case that's useful - this.eventContext = null; - - } catch (SigarException e) { - throw new RuntimeException(e); - } - } - - return this.logFileInfo; - } - @Nullable public Set<Event> poll() { - + if (!this.initialized) { + init(); + } + if (this.logFileInfo == null) { + // This means SIGAR, which we require, is unavailable, so just return null. + return null; + } + if (!this.logFile.exists()) { - log.warn("Log file [" + this.logFile + "' being polled does not exist."); + log.warn("Log file [" + this.logFile + "] being polled does not exist."); return null; } if (this.logFile.isDirectory()) { - log.error("Log file [" + this.logFile + "' being polled is a directory, not a regular file."); + log.error("Log file [" + this.logFile + "] being polled is a directory, not a regular file."); return null; } - FileInfo fileInfo; - try { - fileInfo = getFileInfo(); - if (!fileInfo.changed()) { + + try { + if (!this.logFileInfo.changed()) { return null; } } catch (SigarException e) { throw new RuntimeException(e); } - return processNewLines(fileInfo); + return processNewLines(this.logFileInfo); + } + + /** + * This performs any initialization that requires using the EventContext. It must *not* be called from our + * constructor, because pollers are constructed during PC initialization, and at that time the PC EventManager, + * which the EventContext relies on, is not yet available. Instead it is called from {@link #poll()} on the first + * invocation of that method, at which point the PC will be initialized. + */ + protected void init() { + SigarProxy sigar = this.eventContext.getSigar(); + if (sigar != null) { + try { + this.logFileInfo = new LogFileInfo(sigar.getFileInfo(logFile.getPath())); + } catch (SigarException e) { + throw new RuntimeException("Failed to obtain file info for log file [" + this.logFile + "].", e); + } + } else { + log.warn("SIGAR is unavailable - cannot poll log file [" + this.logFile + "] for events."); + } + + this.initialized = true; }
private Set<Event> processNewLines(FileInfo fileInfo) { diff --git a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventResourceComponentHelper.java b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventResourceComponentHelper.java index 962764a..d134638 100644 --- a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventResourceComponentHelper.java +++ b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/event/log/LogFileEventResourceComponentHelper.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2008 Red Hat, Inc. + * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,9 @@ package org.rhq.core.pluginapi.event.log; import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException;
@@ -32,10 +35,12 @@ import org.rhq.core.domain.configuration.Property; import org.rhq.core.domain.configuration.PropertyList; import org.rhq.core.domain.configuration.PropertyMap; import org.rhq.core.domain.event.EventSeverity; +import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.pluginapi.event.EventContext; import org.rhq.core.pluginapi.event.EventPoller; import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException; import org.rhq.core.pluginapi.inventory.ResourceContext; +import org.rhq.core.system.SystemInfoFactory;
/** * @author Ian Springer @@ -47,83 +52,132 @@ public class LogFileEventResourceComponentHelper { public static final String LOG_EVENT_SOURCE_CONFIG_PROP = "logEventSource";
public abstract static class LogEventSourcePropertyNames { - public static final String LOG_FILE_PATH = "logFilePath"; - public static final String ENABLED = "enabled"; - public static final String DATE_FORMAT = "dateFormat"; - public static final String INCLUDES_PATTERN = "includesPattern"; - public static final String MINIMUM_SEVERITY = "minimumSeverity"; + public static final String LOG_FILE_PATH = "logFilePath"; // required + public static final String ENABLED = "enabled"; // required + public static final String DATE_FORMAT = "dateFormat"; // optional + public static final String INCLUDES_PATTERN = "includesPattern"; // optional + public static final String MINIMUM_SEVERITY = "minimumSeverity"; // optional }
+ // TODO: Make this configurable via a plugin config prop. + private static final int POLLING_INTERVAL_IN_SECONDS = 60; + private final Log log = LogFactory.getLog(this.getClass());
private ResourceContext<?> resourceContext; + private List<PropertyMap> startedEventSources = new ArrayList<PropertyMap>();
public LogFileEventResourceComponentHelper(ResourceContext<?> resourceContext) { this.resourceContext = resourceContext; }
public void startLogFileEventPollers() { + // Grab the list-o-maps of event sources from the plugin config. Configuration pluginConfig = this.resourceContext.getPluginConfiguration(); PropertyList logEventSources = pluginConfig.getList(LOG_EVENT_SOURCES_CONFIG_PROP); + + // Build a new list containing the event sources that are enabled. + List<PropertyMap> enabledEventSources = new ArrayList<PropertyMap>(); for (Property prop : logEventSources.getList()) { PropertyMap logEventSource = (PropertyMap) prop; - Boolean enabled = Boolean.valueOf(logEventSource.getSimpleValue(LogEventSourcePropertyNames.ENABLED, null)); - if (enabled) { - String logFilePathname = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null); - if (logFilePathname == null) { - log.info("LOGFILE: No logfile path given, can not watch this event log."); - return; - } - File logFile = new File(logFilePathname); - if (!logFile.exists() || !logFile.canRead()) { - log.error("LOGFILE: Logfile at location " + logFilePathname - + " does not exist or is not readable. Can not start watching the event log."); - return; - } + String enabled = logEventSource.getSimpleValue(LogEventSourcePropertyNames.ENABLED, null); + if (enabled == null) { + throw new IllegalStateException("Required property [" + LogEventSourcePropertyNames.ENABLED + + "] is not defined in map."); + } + if (Boolean.valueOf(enabled)) { + enabledEventSources.add(logEventSource); + } + }
- Log4JLogEntryProcessor processor = new Log4JLogEntryProcessor(LOG_ENTRY_EVENT_TYPE, logFile); - String dateFormatString = logEventSource.getSimpleValue(LogEventSourcePropertyNames.DATE_FORMAT, null); - if (dateFormatString != null) { - try { - DateFormat dateFormat = new SimpleDateFormat(dateFormatString); // TODO locale specific ? - processor.setDateFormat(dateFormat); - } catch (IllegalArgumentException e) { - throw new InvalidPluginConfigurationException("Date format [" + dateFormatString - + "] is not a valid simple date format."); - } - } - String includesPatternString = logEventSource.getSimpleValue( - LogEventSourcePropertyNames.INCLUDES_PATTERN, null); - if (includesPatternString != null) { - try { - Pattern includesPattern = Pattern.compile(includesPatternString); - processor.setIncludesPattern(includesPattern); - } catch (PatternSyntaxException e) { - throw new InvalidPluginConfigurationException("Includes pattern [" + includesPatternString - + "] is not a valid regular expression."); - } + // Log a warning then return if SIGAR isn't available, since LogFileEventPoller depends on it. We only log this + // warning if at least one event source is enabled, since otherwise the user probably doesn't care. + boolean sigarAvailable = this.resourceContext.getSystemInformation().isNative(); + if (!sigarAvailable && !enabledEventSources.isEmpty()) { + boolean nativeSystemInfoDisabled = SystemInfoFactory.isNativeSystemInfoDisabled(); + ResourceType resourceType = this.resourceContext.getResourceType(); + List<String> logFilePaths = getLogFilePaths(enabledEventSources); + log.warn("Log files " + logFilePaths + " for [" + resourceType.getPlugin() + ":" + + resourceType.getName() + "] Resource with key [" + this.resourceContext.getResourceKey() + + "] cannot be polled, because log file polling requires RHQ native support, which " + + ((nativeSystemInfoDisabled) ? "has been disabled for this Agent" : "is not available on this platform") + "."); + return; + } + + // Start up log file pollers for each of the enabled event sources. + for (PropertyMap logEventSource : enabledEventSources) { + String logFilePath = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null); + if (logFilePath == null) { + throw new IllegalStateException("Required property [" + LogEventSourcePropertyNames.LOG_FILE_PATH + + "] is not defined in map."); + } + File logFile = new File(logFilePath); + if (!logFile.canRead()) { + log.error("LOGFILE: Logfile at location " + logFilePath + + " does not exist or is not readable. Can not start watching the event log."); + continue; + } + + Log4JLogEntryProcessor processor = new Log4JLogEntryProcessor(LOG_ENTRY_EVENT_TYPE, logFile); + String dateFormatString = logEventSource.getSimpleValue(LogEventSourcePropertyNames.DATE_FORMAT, null); + if (dateFormatString != null) { + try { + DateFormat dateFormat = new SimpleDateFormat(dateFormatString); // TODO locale specific ? + processor.setDateFormat(dateFormat); + } catch (IllegalArgumentException e) { + throw new InvalidPluginConfigurationException("Date format [" + dateFormatString + + "] is not a valid simple date format."); } - String minimumSeverityString = logEventSource.getSimpleValue( - LogEventSourcePropertyNames.MINIMUM_SEVERITY, null); - if (minimumSeverityString != null) { - EventSeverity minimumSeverity = EventSeverity.valueOf(minimumSeverityString.toUpperCase()); - processor.setMinimumSeverity(minimumSeverity); + } + String includesPatternString = logEventSource.getSimpleValue( + LogEventSourcePropertyNames.INCLUDES_PATTERN, null); + if (includesPatternString != null) { + try { + Pattern includesPattern = Pattern.compile(includesPatternString); + processor.setIncludesPattern(includesPattern); + } catch (PatternSyntaxException e) { + throw new InvalidPluginConfigurationException("Includes pattern [" + includesPatternString + + "] is not a valid regular expression."); } - EventContext eventContext = this.resourceContext.getEventContext(); - EventPoller poller = new LogFileEventPoller(eventContext, LOG_ENTRY_EVENT_TYPE, logFile, processor); - eventContext.registerEventPoller(poller, 60, logFile.getPath()); } + String minimumSeverityString = logEventSource.getSimpleValue( + LogEventSourcePropertyNames.MINIMUM_SEVERITY, null); + if (minimumSeverityString != null) { + EventSeverity minimumSeverity = EventSeverity.valueOf(minimumSeverityString.toUpperCase()); + processor.setMinimumSeverity(minimumSeverity); + } + EventContext eventContext = this.resourceContext.getEventContext(); + EventPoller poller = new LogFileEventPoller(eventContext, LOG_ENTRY_EVENT_TYPE, logFile, processor); + eventContext.registerEventPoller(poller, POLLING_INTERVAL_IN_SECONDS, logFile.getPath()); + this.startedEventSources.add(logEventSource); } }
public void stopLogFileEventPollers() { - Configuration pluginConfig = this.resourceContext.getPluginConfiguration(); - PropertyList logEventSources = pluginConfig.getList(LOG_EVENT_SOURCES_CONFIG_PROP); - for (Property prop : logEventSources.getList()) { - PropertyMap logEventSource = (PropertyMap) prop; - String logFilePath = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null); + boolean sigarAvailable = this.resourceContext.getSystemInformation().isNative(); + if (!sigarAvailable) { + return; + } + + for (Iterator<PropertyMap> iterator = this.startedEventSources.iterator(); iterator.hasNext(); ) { + PropertyMap logEventSource = iterator.next(); EventContext eventContext = this.resourceContext.getEventContext(); + String logFilePath = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null); eventContext.unregisterEventPoller(LOG_ENTRY_EVENT_TYPE, logFilePath); + iterator.remove(); + } + } + + private List<String> getLogFilePaths(List<PropertyMap> enabledEventSources) { + List<String> logFilePaths = new ArrayList<String>(enabledEventSources.size()); + for (PropertyMap logEventSource : enabledEventSources) { + String logFilePath = logEventSource.getSimpleValue(LogEventSourcePropertyNames.LOG_FILE_PATH, null); + if (logFilePath == null) { + throw new IllegalStateException("Required property [" + LogEventSourcePropertyNames.LOG_FILE_PATH + + "] is not defined in map."); + } + logFilePaths.add(logFilePath); } + return logFilePaths; } } diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventContextImpl.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventContextImpl.java index b0b37ec..1735aff 100644 --- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventContextImpl.java +++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventContextImpl.java @@ -24,6 +24,7 @@ package org.rhq.core.pc.event;
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.resource.Resource; @@ -99,7 +100,7 @@ public class EventContextImpl implements EventContext { unregisterEventPollerInternal(eventType, sourceLocation); }
- @NotNull + @Nullable public SigarProxy getSigar() { return getEventManager().getSigar(); } diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventManager.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventManager.java index 4ad5c50..87b9bd7 100644 --- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventManager.java +++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/event/EventManager.java @@ -34,6 +34,7 @@ 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; @@ -44,6 +45,7 @@ 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; +import org.rhq.core.system.SystemInfoException;
/** * Manager for the Plugin Container's Event subsystem. @@ -113,9 +115,12 @@ public class EventManager implements ContainerService { } }
+ @Nullable SigarProxy getSigar() { if (this.sigar == null) { - this.sigar = SigarAccess.getSigar(); + if (SigarAccess.isSigarAvailable()) { + this.sigar = SigarAccess.getSigar(); + } } return this.sigar; }
rhq-commits@lists.fedorahosted.org