| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the License. You may obtain a |
| * copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package org.apache.geode.internal.admin; |
| |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TimerTask; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.CancelException; |
| import org.apache.geode.StatisticDescriptor; |
| import org.apache.geode.Statistics; |
| import org.apache.geode.StatisticsType; |
| import org.apache.geode.admin.jmx.internal.StatAlertsAggregator; |
| import org.apache.geode.annotations.internal.MakeNotStatic; |
| import org.apache.geode.distributed.internal.ClusterDistributionManager; |
| import org.apache.geode.distributed.internal.InternalDistributedSystem; |
| import org.apache.geode.internal.SystemTimer; |
| import org.apache.geode.internal.SystemTimer.SystemTimerTask; |
| import org.apache.geode.internal.admin.remote.AlertsNotificationMessage; |
| import org.apache.geode.internal.admin.remote.UpdateAlertDefinitionMessage; |
| import org.apache.geode.internal.admin.statalerts.DummyStatisticInfoImpl; |
| import org.apache.geode.internal.admin.statalerts.StatisticInfo; |
| import org.apache.geode.internal.admin.statalerts.StatisticInfoImpl; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| |
| /** |
| * The alert manager maintains the list of alert definitions (added by client e.g GFMon 2.0). |
| * |
| * It retrieved the value of statistic( defined in alert definition) and notify alert aggregator |
| * sitting on admin VM |
| * |
| * @see StatAlertDefinition |
| * @see StatAlert |
| * |
| * |
| * @since GemFire 5.7 |
| */ |
| public class StatAlertsManager { |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** |
| * Instance for current DM |
| * |
| * Guarded by StatAlertsManager.class |
| */ |
| @MakeNotStatic |
| private static StatAlertsManager alertManager; |
| |
| /** |
| * Guarded by this |
| */ |
| private long refreshInterval; |
| |
| /** |
| * Guarded by this.alertDefinitionsMap |
| */ |
| @MakeNotStatic |
| protected final HashMap alertDefinitionsMap = new HashMap(); |
| |
| /** |
| * Guarded by this |
| */ |
| private SystemTimer timer; |
| |
| /** |
| * Guarded by this |
| */ |
| private boolean refreshAtFixedRate; |
| |
| /** |
| * Provides life cycle support |
| */ |
| protected final ClusterDistributionManager dm; |
| |
| private StatAlertsManager(ClusterDistributionManager dm) { |
| this.dm = dm; |
| logger.info("StatAlertsManager created"); |
| } |
| |
| /** |
| * @return singleton instance of StatAlertsManager |
| */ |
| public static synchronized StatAlertsManager getInstance(ClusterDistributionManager dm) { |
| // As per current implementation set up request will be send only once , |
| // when member joined to Admin distributed system |
| // we don't need to care about race condition |
| if (alertManager != null && alertManager.dm == dm) { |
| return alertManager; |
| } |
| if (alertManager != null) { |
| alertManager.close(); |
| } |
| |
| /* |
| * Throw DistributedSystemDisconnectedException if cancel operation is in progress |
| */ |
| dm.getCancelCriterion().checkCancelInProgress(null); |
| |
| alertManager = new StatAlertsManager(dm); |
| return alertManager; |
| } |
| |
| /** |
| * Nullifies the StatAlertsManager instance. |
| */ |
| private static synchronized void closeInstance() { |
| StatAlertsManager.alertManager = null; |
| } |
| |
| /** |
| * |
| * Update the alert's definition map |
| * |
| * @param defns Alert definitions |
| * @param actionCode Action to be performed like add , remove or update alert's definition |
| * |
| * @see UpdateAlertDefinitionMessage |
| */ |
| public void updateAlertDefinition(StatAlertDefinition[] defns, int actionCode) { |
| if (logger.isDebugEnabled()) { |
| logger.debug("Entered StatAlertsManager.updateAlertDefinition *****"); |
| } |
| synchronized (alertDefinitionsMap) { |
| if (actionCode == UpdateAlertDefinitionMessage.REMOVE_ALERT_DEFINITION) { |
| for (int i = 0; i < defns.length; i++) { |
| alertDefinitionsMap.remove(Integer.valueOf(defns[i].getId())); |
| if (logger.isDebugEnabled()) { |
| logger.debug("Removed StatAlertDefinition: {}", defns[i].getName()); |
| } |
| } |
| } else { |
| StatAlertDefinition[] alertDefns = this.createMemberStatAlertDefinition(dm, defns); |
| StatAlertDefinition defn; |
| for (int i = 0; i < alertDefns.length; i++) { |
| defn = alertDefns[i]; |
| alertDefinitionsMap.put(Integer.valueOf(defns[i].getId()), defn); |
| } |
| } |
| } // synchronized |
| |
| if (logger.isDebugEnabled()) { |
| logger.debug("Exiting StatAlertsManager.updateAlertDefinition *****"); |
| } |
| } |
| |
| private synchronized void rescheduleTimer() { |
| // cancel the old timer. Although cancelled, old task might execute one last |
| // time |
| if (timer != null) |
| timer.cancel(); |
| |
| // Get the swarm. Currently rather UGLY. |
| InternalDistributedSystem system = dm.getSystem(); |
| if (system == null || system.getDistributionManager() != dm) { |
| throw new org.apache.geode.distributed.DistributedSystemDisconnectedException( |
| "This manager has been cancelled"); |
| } |
| // start and schedule new timer |
| timer = new SystemTimer(system /* swarm */, true); |
| |
| EvaluateAlertDefnsTask task = new EvaluateAlertDefnsTask(); |
| if (refreshAtFixedRate) { |
| timer.scheduleAtFixedRate(task, 0, refreshInterval); |
| } else { |
| timer.schedule(task, 0, refreshInterval); |
| } |
| } |
| |
| /** |
| * Set refresh time interval also cancel the previous {@link TimerTask} and create new timer task |
| * based on ner refresh time interval |
| * |
| * @param interval Refresh time interval |
| */ |
| public synchronized void setRefreshTimeInterval(long interval) { |
| refreshInterval = interval; |
| rescheduleTimer(); |
| } |
| |
| /** |
| * |
| * @return time interval alert generation |
| */ |
| public synchronized long getRefreshTimeInterval() { |
| return refreshInterval; |
| } |
| |
| /** |
| * @return true if refresh for timer has to be fixed rate see scheduleAtFixedRate method of |
| * {@link TimerTask} |
| */ |
| public synchronized boolean isRefreshAtFixedRate() { |
| return refreshAtFixedRate; |
| } |
| |
| /** |
| * set true if refresh for timer has to be fixed rate see scheduleAtFixedRate method of |
| * {@link TimerTask} |
| * |
| * TODO never called |
| * |
| */ |
| public synchronized void setRefreshAtFixedRate(boolean refreshAtFixedRate) { |
| this.refreshAtFixedRate = refreshAtFixedRate; |
| rescheduleTimer(); |
| } |
| |
| /** |
| * Query all the statistic defined by alert definition and notify alerts aggregator if at least |
| * one statistic value crosses the threshold defined in alert definition |
| * |
| */ |
| protected StatAlert[] getAlerts() { |
| Set alerts = new HashSet(); |
| |
| synchronized (alertDefinitionsMap) { |
| Set keyset = alertDefinitionsMap.keySet(); |
| Iterator iter = keyset.iterator(); |
| StatAlert alert; |
| Date now = new Date(); |
| while (iter.hasNext()) { |
| Integer key = (Integer) iter.next(); |
| StatAlertDefinition defn = (StatAlertDefinition) alertDefinitionsMap.get(key); |
| alert = defn.evaluateAndAlert(); |
| if (alert != null) { |
| alert.setTime(now); |
| alerts.add(alert); |
| if (logger.isDebugEnabled()) { |
| logger.debug("getAlerts: found alert {}", alert); |
| } |
| } |
| } // while |
| } // synchronized |
| return (StatAlert[]) alerts.toArray(new StatAlert[0]); |
| } |
| |
| /** |
| * Convert {@link StatAlertDefinition }(Created by client like GFMon2.0) with |
| * {@link DummyStatisticInfoImpl} to StatAlertDefinition with {@link StatisticInfoImpl} |
| */ |
| private StatAlertDefinition[] createMemberStatAlertDefinition(ClusterDistributionManager dm, |
| StatAlertDefinition[] defns) { |
| dm.getCancelCriterion().checkCancelInProgress(null); |
| |
| Statistics[] statistics; |
| StatisticsType type; |
| StatisticDescriptor desc; |
| String textId; |
| boolean skipDefinition = false; |
| List result = new ArrayList(); |
| |
| for (int i = 0; i < defns.length; i++) { |
| skipDefinition = false; |
| StatAlertDefinition defn = defns[i]; |
| StatisticInfo[] statInfos = defn.getStatisticInfo(); |
| for (int ii = 0; ii < statInfos.length && !skipDefinition; ii++) { |
| textId = statInfos[ii].getStatisticsTextId(); |
| |
| // TODO If none by TextID, use StatType and getAll. |
| statistics = dm.getSystem().findStatisticsByTextId(textId); |
| if (statistics.length == 0) { |
| logger.error( |
| "StatAlertsManager.createMemberStatAlertDefinition :: statistics with given textId={}, NOT found.", |
| textId); |
| skipDefinition = true; |
| // break; |
| continue; // To print all errors |
| } |
| |
| type = statistics[0].getType(); |
| desc = type.nameToDescriptor(statInfos[ii].getStatisticName()); |
| // Replace the actual StatInfo object |
| statInfos[ii] = new StatisticInfoImpl(statistics[0], desc); |
| if (logger.isDebugEnabled()) { |
| logger.debug("StatAlertsManager.createMemberStatAlertDefinition: created statInfo {}", |
| statInfos[ii]); |
| } |
| } // for |
| |
| if (!skipDefinition) { |
| defn.setStatisticInfo(statInfos); |
| result.add(defn); |
| if (logger.isDebugEnabled()) { |
| logger.debug("StatAlertsManager.createMemberStatAlertDefinition :: {}", |
| defns[i].getStringRepresentation()); |
| } |
| } else { |
| if (logger.isDebugEnabled()) { |
| logger.debug( |
| "StatAlertsManager.createMemberStatAlertDefinition :: StatAlertDefinition {} is excluded", |
| defn.getName()); |
| } |
| } |
| } // for |
| |
| return (StatAlertDefinition[]) result.toArray(new StatAlertDefinition[0]); |
| } |
| |
| /** |
| * Shut down this instance |
| */ |
| protected synchronized void close() { |
| // nullify the manager instance first |
| closeInstance(); |
| |
| // cancel the old timer. Although canceled, old task might execute one last |
| // time |
| if (timer != null) { |
| timer.cancel(); |
| } |
| timer = null; |
| } |
| |
| /** |
| * Timer task to send all the alerts raised to {@link StatAlertsAggregator} |
| * |
| */ |
| class EvaluateAlertDefnsTask extends SystemTimerTask { |
| /** |
| * Collect all the alerts raised and send it to {@link StatAlertsAggregator} |
| */ |
| @Override |
| public void run2() { |
| final boolean isDebugEnabled = logger.isDebugEnabled(); |
| |
| synchronized (StatAlertsManager.this) { |
| if (dm.getCancelCriterion().isCancelInProgress()) { |
| return; |
| } |
| |
| // start alert notification are supposed to send to all the |
| // admin agents exists in the system. |
| // For the DS without agent, alert manager should not create |
| // any alert notifications |
| Set adminMemberSet = dm.getAdminMemberSet(); |
| if (adminMemberSet == null || adminMemberSet.isEmpty()) |
| return; |
| |
| if (isDebugEnabled) { |
| logger.debug("EvaluateAlertDefnsTask: starting"); |
| } |
| try { |
| StatAlert[] alerts = getAlerts(); |
| if (alerts.length == 0) { |
| if (isDebugEnabled) { |
| logger.debug("EvaluateAlertsDefnsTask: no alerts"); |
| } |
| return; |
| } |
| |
| AlertsNotificationMessage request = new AlertsNotificationMessage(); |
| request.setAlerts(alerts); |
| if (isDebugEnabled) { |
| Iterator iterator = adminMemberSet.iterator(); |
| while (iterator.hasNext()) { |
| logger.debug("EvaluateAlertDefnsTask: sending {} alerts to {}", alerts.length, |
| iterator.next()); |
| } |
| } |
| request.setRecipients(adminMemberSet); |
| dm.putOutgoing(request); |
| } catch (CancelException e) { |
| logger.debug("EvaluateAlertDefnsTask: system closed: {}", e.getMessage(), e); |
| close(); |
| } catch (Exception e) { |
| logger.error("EvaluateAlertDefnsTask failed with an exception", |
| e); |
| close(); |
| } |
| if (isDebugEnabled) { |
| logger.debug("EvaluateAlertDefnsTask: done "); |
| } |
| } |
| } // run |
| } // EvaluateAlertDefnsTask |
| } |