/* * RHQ Management Platform * Copyright (C) 2005-2008 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.alert.engine.internal; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import org.rhq.core.domain.alert.AlertCondition; import org.rhq.core.domain.alert.AlertConditionCategory; import org.rhq.core.domain.alert.AlertConditionOperator; import org.rhq.core.domain.alert.composite.AbstractAlertConditionCategoryComposite; import org.rhq.core.domain.alert.composite.AlertConditionAvailabilityCategoryComposite; import org.rhq.core.domain.alert.composite.AlertConditionControlCategoryComposite; import org.rhq.core.domain.alert.composite.AlertConditionResourceConfigurationCategoryComposite; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.configuration.ResourceConfigurationUpdate; import org.rhq.core.domain.measurement.Availability; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.domain.operation.OperationDefinition; import org.rhq.core.domain.operation.OperationHistory; import org.rhq.core.domain.operation.OperationRequestStatus; import org.rhq.core.domain.operation.ResourceOperationHistory; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; import org.rhq.enterprise.server.alert.AlertConditionManagerLocal; import org.rhq.enterprise.server.alert.engine.AlertConditionCacheStats; import org.rhq.enterprise.server.alert.engine.internal.AlertConditionCacheCoordinator.Cache; import org.rhq.enterprise.server.alert.engine.mbean.AlertConditionCacheMonitor; import org.rhq.enterprise.server.alert.engine.model.AvailabilityCacheElement; import org.rhq.enterprise.server.alert.engine.model.AvailabilityDurationCacheElement; import org.rhq.enterprise.server.alert.engine.model.AvailabilityDurationComposite; import org.rhq.enterprise.server.alert.engine.model.InvalidCacheElementException; import org.rhq.enterprise.server.alert.engine.model.ResourceConfigurationCacheElement; import org.rhq.enterprise.server.alert.engine.model.ResourceOperationCacheElement; import org.rhq.enterprise.server.auth.SubjectManagerLocal; import org.rhq.enterprise.server.util.LookupUtil; /** * @author Joseph Marques */ class GlobalConditionCache extends AbstractConditionCache { private Map<Integer, Map<Integer, List<ResourceOperationCacheElement>>> resourceOperationCache; // key: resource ID, inner key: operation def ID private Map<Integer, List<AvailabilityCacheElement>> availabilityCache; // key: resource ID private Map<Integer, List<AvailabilityDurationCacheElement>> availabilityDurationCache; // key: resource ID private Map<Integer, List<ResourceConfigurationCacheElement>> resourceConfigurationCache; // key: resource ID private AlertConditionManagerLocal alertConditionManager; private SubjectManagerLocal subjectManager; public GlobalConditionCache() { super(); resourceOperationCache = new HashMap<Integer, Map<Integer, List<ResourceOperationCacheElement>>>(); availabilityCache = new HashMap<Integer, List<AvailabilityCacheElement>>(); availabilityDurationCache = new HashMap<Integer, List<AvailabilityDurationCacheElement>>(); resourceConfigurationCache = new HashMap<Integer, List<ResourceConfigurationCacheElement>>(); alertConditionManager = LookupUtil.getAlertConditionManager(); subjectManager = LookupUtil.getSubjectManager(); loadCaches(); } /** * @return the number of conditions that re/loaded */ private AlertConditionCacheStats loadCaches() { AlertConditionCacheStats stats = new AlertConditionCacheStats(); try { log.debug("Loading Global Condition Cache..."); Subject overlord = subjectManager.getOverlord(); EnumSet<AlertConditionCategory> supportedCategories = EnumSet.of(AlertConditionCategory.AVAILABILITY, AlertConditionCategory.AVAIL_DURATION, AlertConditionCategory.CONTROL, AlertConditionCategory.RESOURCE_CONFIG); for (AlertConditionCategory nextCategory : supportedCategories) { // page thru all alert definitions int rowsProcessed = 0; PageControl pc = new PageControl(); pc.setPageNumber(0); pc.setPageSize(PAGE_SIZE); // condition composites are small so we can grab alot; use the setter, constructor limits this to 100 while (true) { PageList<? extends AbstractAlertConditionCategoryComposite> alertConditions = null; alertConditions = alertConditionManager.getAlertConditionComposites(overlord, null, nextCategory, pc); if (alertConditions.isEmpty()) { break; // didn't get any rows back, must not have any data or no more rows left to process } for (AbstractAlertConditionCategoryComposite nextComposite : alertConditions) { insertAlertConditionComposite(nextComposite, stats); } rowsProcessed += alertConditions.size(); if (rowsProcessed >= alertConditions.getTotalSize()) { break; // we've processed all data, we can stop now } pc.setPageNumber(pc.getPageNumber() + 1); } if (log.isDebugEnabled()) log.debug("Loaded " + rowsProcessed + " Alert Condition Composites of type '" + nextCategory + "'"); } log.debug("Loaded Global Condition Cache"); } catch (Throwable t) { // don't let any exceptions bubble up to the calling SLSB layer log.error("Error loading global condition cache", t); } return stats; } private void insertAlertConditionComposite(AbstractAlertConditionCategoryComposite composite, AlertConditionCacheStats stats) { AlertCondition alertCondition = composite.getCondition(); int alertConditionId = alertCondition.getId(); // auto-unboxing is safe here because as the PK it's guaranteed to be non-null AlertConditionCategory alertConditionCategory = alertCondition.getCategory(); AlertConditionOperator alertConditionOperator = AlertConditionCacheUtils .getAlertConditionOperator(alertCondition); if (alertConditionCategory == AlertConditionCategory.AVAILABILITY) { AlertConditionAvailabilityCategoryComposite availabilityComposite = (AlertConditionAvailabilityCategoryComposite) composite; try { AvailabilityCacheElement cacheElement = new AvailabilityCacheElement(alertConditionOperator, availabilityComposite.getAvailabilityType(), alertConditionId); addTo("availabilityCache", availabilityCache, availabilityComposite.getResourceId(), cacheElement, alertConditionId, stats); } catch (InvalidCacheElementException icee) { log.info("Failed to create AvailabilityCacheElement with parameters: " + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator, availabilityComposite.getAvailabilityType(), AvailabilityType.UP, icee)); } } else if (alertConditionCategory == AlertConditionCategory.AVAIL_DURATION) { AlertConditionAvailabilityCategoryComposite availabilityComposite = (AlertConditionAvailabilityCategoryComposite) composite; try { AvailabilityDurationCacheElement cacheElement = new AvailabilityDurationCacheElement( availabilityComposite.getAlertDefinitionId(), alertConditionOperator, alertCondition.getOption(), availabilityComposite.getAvailabilityType(), alertConditionId); addTo("availabilityDurationCache", availabilityDurationCache, availabilityComposite.getResourceId(), cacheElement, alertConditionId, stats); } catch (InvalidCacheElementException icee) { log.info("Failed to create AvailabilityCacheElement with parameters: " + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator, availabilityComposite.getAvailabilityType(), alertConditionOperator.toString(), icee)); } } else if (alertConditionCategory == AlertConditionCategory.CONTROL) { AlertConditionControlCategoryComposite controlComposite = (AlertConditionControlCategoryComposite) composite; String option = alertCondition.getOption(); OperationRequestStatus operationRequestStatus = OperationRequestStatus.valueOf(option.toUpperCase()); try { ResourceOperationCacheElement cacheElement = new ResourceOperationCacheElement(alertConditionOperator, operationRequestStatus, alertConditionId); // auto-boxing always safe addToResourceOperationCache(controlComposite.getResourceId(), controlComposite.getOperationDefinitionId(), cacheElement, alertConditionId, stats); } catch (InvalidCacheElementException icee) { log.info("Failed to create ResourceOperationCacheElement with parameters: " + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator, null, operationRequestStatus, icee)); } } else if (alertConditionCategory == AlertConditionCategory.RESOURCE_CONFIG) { AlertConditionResourceConfigurationCategoryComposite resourceConfigurationComposite = (AlertConditionResourceConfigurationCategoryComposite) composite; ResourceConfigurationCacheElement cacheElement = null; try { cacheElement = new ResourceConfigurationCacheElement(alertConditionOperator, resourceConfigurationComposite.getResourceConfiguration(), alertConditionId); } catch (InvalidCacheElementException icee) { log.info("Failed to create EventCacheElement with parameters: " + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator, null, null, icee)); } addTo("resourceConfigurationCache", resourceConfigurationCache, resourceConfigurationComposite.getResourceId(), cacheElement, alertConditionId, stats); } } public AlertConditionCacheStats checkConditions(OperationHistory operationHistory) { AlertConditionCacheStats stats = new AlertConditionCacheStats(); try { if (operationHistory instanceof ResourceOperationHistory) { ResourceOperationHistory resourceOperationHistory = (ResourceOperationHistory) operationHistory; Resource resource = resourceOperationHistory.getResource(); OperationDefinition operationDefinition = resourceOperationHistory.getOperationDefinition(); OperationRequestStatus operationStatus = resourceOperationHistory.getStatus(); List<ResourceOperationCacheElement> cacheElements = lookupResourceOperationHistoryCacheElements( resource.getId(), operationDefinition.getId()); processCacheElements(cacheElements, operationStatus, resourceOperationHistory.getModifiedTime(), stats); } else { if (log.isDebugEnabled()) log.debug(getClass().getSimpleName() + " does not support checking conditions against " + operationHistory.getClass().getSimpleName() + " types"); } AlertConditionCacheMonitor.getMBean().incrementOperationCacheElementMatches(stats.matched); AlertConditionCacheMonitor.getMBean().incrementOperationProcessingTime(stats.getAge()); if (log.isDebugEnabled()) log.debug("Check OperationHistory[size=1] - " + stats); } catch (Throwable t) { // don't let any exceptions bubble up to the calling SLSB layer log.error("Error during global cache processing: ", t); } return stats; } public AlertConditionCacheStats checkConditions(ResourceConfigurationUpdate update) { if (update == null) { return new AlertConditionCacheStats(); } AlertConditionCacheStats stats = new AlertConditionCacheStats(); try { Resource resource = update.getResource(); List<ResourceConfigurationCacheElement> cacheElements = lookupResourceConfigurationCacheElements(resource .getId()); processCacheElements(cacheElements, update.getConfiguration(), update.getCreatedTime(), stats); AlertConditionCacheMonitor.getMBean().incrementResourceConfigurationCacheElementMatches(stats.matched); AlertConditionCacheMonitor.getMBean().incrementResourceConfigurationProcessingTime(stats.getAge()); if (log.isDebugEnabled()) log.debug("Check " + update + " - " + stats); } catch (Throwable t) { // don't let any exceptions bubble up to the calling SLSB layer log.error("Error during global cache processing: ", t); } return stats; } public AlertConditionCacheStats checkConditions(Availability... availabilities) { if ((availabilities == null) || (availabilities.length == 0)) { return new AlertConditionCacheStats(); } AlertConditionCacheStats stats = new AlertConditionCacheStats(); try { for (Availability availability : availabilities) { Resource resource = availability.getResource(); AvailabilityType availabilityType = availability.getAvailabilityType(); List<AvailabilityCacheElement> cacheElements = lookupAvailabilityCacheElements(resource.getId()); processCacheElements(cacheElements, availabilityType, availability.getStartTime(), stats); // Avail Duration conditions are evaluated in two parts: // 1) First, an avail change that starts the clock ticking. // 2) Second, after the duration period, check to see if the avail state is still the same. // Here we check for part 1, see if we need to start duration processing for the avail change. If so, // make sure we capture the avail start time, which is an agent time, not a server time, so we can // correctly check for avail changes later (BZ 1099114). List<AvailabilityDurationCacheElement> durationCacheElements = lookupAvailabilityDurationCacheElements(resource .getId()); AvailabilityDurationCacheElement.checkCacheElements(durationCacheElements, resource, availability); } AlertConditionCacheMonitor.getMBean().incrementAvailabilityCacheElementMatches(stats.matched); AlertConditionCacheMonitor.getMBean().incrementAvailabilityProcessingTime(stats.getAge()); if (log.isDebugEnabled()) log.debug("Check Availability[size=" + availabilities.length + "] - " + stats); } catch (Throwable t) { // don't let any exceptions bubble up to the calling SLSB layer log.error("Error during global cache processing: ", t); } return stats; } // Avail Duration conditions are evaluated in two parts: // 1) First, an avail change to the that starts the clock ticking. // 2) Second, after the duration period, check to see if the avail state is still the same. // Here we check for part 2, finish processing of the condition whose duration job finished and // determined the avail state to be satisfied. Now hook into the alerting chassis... public AlertConditionCacheStats checkConditions(AvailabilityDurationComposite... composites) { if ((null == composites) || (composites.length == 0)) { return new AlertConditionCacheStats(); } AlertConditionCacheStats stats = new AlertConditionCacheStats(); try { for (AvailabilityDurationComposite composite : composites) { List<AvailabilityDurationCacheElement> cacheElements = lookupAvailabilityDurationCacheElements(composite .getResourceId()); // This method differs from the other <code>checkConditions<code> methods in that it is only // interested in a single condition for each composite, the one for which the duration job completed. if (!(null == cacheElements || cacheElements.isEmpty())) { for (AvailabilityDurationCacheElement cacheElement : cacheElements) { if (composite.getConditionId() == cacheElement.getAlertConditionTriggerId()) { List<AvailabilityDurationCacheElement> cacheElementAsList = new ArrayList<AvailabilityDurationCacheElement>( 1); cacheElementAsList.add(cacheElement); processCacheElements(cacheElementAsList, composite.getAvailabilityType(), System.currentTimeMillis(), stats); break; } } } } AlertConditionCacheMonitor.getMBean().incrementAvailabilityDurationCacheElementMatches(stats.matched); AlertConditionCacheMonitor.getMBean().incrementAvailabilityDurationProcessingTime(stats.getAge()); if (log.isDebugEnabled()) log.debug("Check AvailabilityDuration[size=" + composites.length + "] - " + stats); } catch (Throwable t) { // don't let any exceptions bubble up to the calling SLSB layer log.error("Error during global cache processing: ", t); } return stats; } private boolean addToResourceOperationCache(Integer resourceId, Integer operationDefinitionId, ResourceOperationCacheElement cacheElement, Integer alertConditionId, AlertConditionCacheStats stats) { // resourceOperationCache = new HashMap< Integer, Map< Integer, List < ResourceOperationCacheElement > > >(); Map<Integer, List<ResourceOperationCacheElement>> operationDefinitionMap = resourceOperationCache .get(resourceId); if (operationDefinitionMap == null) { operationDefinitionMap = new HashMap<Integer, List<ResourceOperationCacheElement>>(); resourceOperationCache.put(resourceId, operationDefinitionMap); } return addTo("operationDefinitionMap", operationDefinitionMap, operationDefinitionId, cacheElement, alertConditionId, stats); } private List<ResourceOperationCacheElement> lookupResourceOperationHistoryCacheElements(int resourceId, int definitionId) { Map<Integer, List<ResourceOperationCacheElement>> operationDefinitionMap = resourceOperationCache .get(resourceId); if (operationDefinitionMap == null) { return null; } return operationDefinitionMap.get(definitionId); // yup, might be null } private List<AvailabilityCacheElement> lookupAvailabilityCacheElements(int resourceId) { return availabilityCache.get(resourceId); // yup, might be null } private List<AvailabilityDurationCacheElement> lookupAvailabilityDurationCacheElements(int resourceId) { return availabilityDurationCache.get(resourceId); // yup, might be null } private List<ResourceConfigurationCacheElement> lookupResourceConfigurationCacheElements(int resourceId) { return resourceConfigurationCache.get(resourceId); // yup, might be null } @Override public int getCacheSize(Cache cache) { if (cache == AlertConditionCacheCoordinator.Cache.AvailabilityCache) { return AlertConditionCacheUtils.getMapListCount(availabilityCache); } else if (cache == AlertConditionCacheCoordinator.Cache.AvailabilityDurationCache) { return AlertConditionCacheUtils.getMapListCount(availabilityDurationCache); } else if (cache == AlertConditionCacheCoordinator.Cache.ResourceConfigurationCache) { return AlertConditionCacheUtils.getMapListCount(resourceConfigurationCache); } else if (cache == AlertConditionCacheCoordinator.Cache.ResourceOperationCache) { return AlertConditionCacheUtils.getMapMapListCount(resourceOperationCache); } else { throw new IllegalArgumentException("The " + GlobalConditionCache.class.getSimpleName() + " either does not manage caches of type " + cache.type + ", or does not support obtaining their size"); } } }