View Javadoc

1   package org.apache.helix.monitoring.mbeans;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.StringWriter;
23  import java.lang.management.ManagementFactory;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Date;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.concurrent.ConcurrentHashMap;
32  
33  import javax.management.MBeanServer;
34  import javax.management.ObjectName;
35  
36  import org.apache.helix.PropertyType;
37  import org.apache.helix.ZNRecord;
38  import org.apache.helix.alerts.AlertParser;
39  import org.apache.helix.alerts.AlertValueAndStatus;
40  import org.apache.helix.alerts.Tuple;
41  import org.apache.log4j.Logger;
42  import org.codehaus.jackson.map.ObjectMapper;
43  import org.codehaus.jackson.map.SerializationConfig;
44  
45  
46  public class ClusterAlertMBeanCollection
47  {
48    public static String DOMAIN_ALERT = "HelixAlerts";
49    public static String ALERT_SUMMARY = "AlertSummary";
50    
51    private static final Logger _logger = Logger.getLogger(ClusterAlertMBeanCollection.class);
52    ConcurrentHashMap<String, ClusterAlertItem> _alertBeans 
53      = new ConcurrentHashMap<String, ClusterAlertItem>();
54    
55    Map<String, String> _recentAlertDelta;
56    ClusterAlertSummary _clusterAlertSummary;
57    ZNRecord _alertHistory = new ZNRecord(PropertyType.ALERT_HISTORY.toString());
58    Set<String> _previousFiredAlerts = new HashSet<String>();
59    // 5 min for mbean freshness threshold
60    public static final long ALERT_NOCHANGE_THRESHOLD = 5 * 60 * 1000;
61      
62    final MBeanServer _beanServer;
63    
64    public interface ClusterAlertSummaryMBean extends ClusterAlertItemMBean
65    {
66      public String getAlertFiredHistory();
67    }
68    
69    class ClusterAlertSummary extends ClusterAlertItem implements ClusterAlertSummaryMBean
70    {
71      public ClusterAlertSummary(String name, AlertValueAndStatus valueAndStatus)
72      {
73        super(name, valueAndStatus);
74      }
75      /**
76       * Returns the previous 100 alert mbean turn on / off history
77       * */
78      @Override
79      public String getAlertFiredHistory()
80      {
81        try
82        {
83          ObjectMapper mapper = new ObjectMapper();
84          SerializationConfig serializationConfig = mapper.getSerializationConfig();
85          serializationConfig.set(SerializationConfig.Feature.INDENT_OUTPUT, true);
86          StringWriter sw = new StringWriter();
87          mapper.writeValue(sw, _alertHistory);
88          return sw.toString();
89        }
90        catch(Exception e)
91        {
92          _logger.warn("", e);
93          return "";
94        }
95      }
96    }
97    
98    
99    public ClusterAlertMBeanCollection()
100   {
101     _beanServer = ManagementFactory.getPlatformMBeanServer();
102   }
103   
104   public Collection<ClusterAlertItemMBean> getCurrentAlertMBeans()
105   {
106     ArrayList<ClusterAlertItemMBean> beans = new ArrayList<ClusterAlertItemMBean>();
107     for(ClusterAlertItem item : _alertBeans.values())
108     {
109       beans.add(item);
110     }
111     return beans;
112   }
113 
114   void onNewAlertMbeanAdded(ClusterAlertItemMBean bean)
115   {
116     try
117     {
118       _logger.info("alert bean " + bean.getSensorName()+" exposed to jmx");
119       System.out.println("alert bean " + bean.getSensorName()+" exposed to jmx");
120       ObjectName objectName =  new ObjectName(DOMAIN_ALERT+":alert="+bean.getSensorName());
121       register(bean, objectName);
122     } 
123     catch (Exception e)
124     {
125       _logger.error("", e);
126       e.printStackTrace();
127     }
128   }
129   
130   public void setAlerts(String originAlert, Map<String, AlertValueAndStatus> alertResultMap, String clusterName)
131   {
132     if(alertResultMap == null)
133     {
134       _logger.warn("null alertResultMap");
135       return;
136     }
137     for(String alertName : alertResultMap.keySet())
138     {
139       String beanName = "";
140       if(alertName.length() > 1)
141       {
142         String comparator = AlertParser.getComponent(AlertParser.COMPARATOR_NAME, originAlert);
143         String constant = AlertParser.getComponent(AlertParser.CONSTANT_NAME, originAlert);
144         beanName = "("+ alertName+")" + comparator + "("+constant+")";
145       }
146       else
147       {
148         beanName = originAlert + "--(" + alertName + ")";
149       }
150       // This is to make JMX happy; certain charaters cannot be in JMX bean name
151       beanName = beanName.replace('*', '%').replace('=', '#').replace(',', ';');
152       if(!_alertBeans.containsKey(beanName))
153       {
154         ClusterAlertItem item = new ClusterAlertItem(beanName, alertResultMap.get(alertName));
155         onNewAlertMbeanAdded(item);
156         _alertBeans.put(beanName, item);
157       }
158       else
159       {
160         _alertBeans.get(beanName).setValueMap(alertResultMap.get(alertName));
161       }
162     }
163     refreshSummayAlert(clusterName);
164   }
165   
166   public void setAlertHistory(ZNRecord alertHistory)
167   {
168     _alertHistory = alertHistory;
169   }
170   /**
171    *  The summary alert is a combination of all alerts, if it is on, something is wrong on this 
172    *  cluster. The additional info contains all alert mbean names that has been fired.
173    */
174   void refreshSummayAlert(String clusterName)
175   {
176     boolean fired = false;
177     String alertsFired = "";
178     String summaryKey = ALERT_SUMMARY + "_" + clusterName;
179     for(String key : _alertBeans.keySet())
180     {
181       if(!key.equals(summaryKey))
182       {
183         ClusterAlertItem item = _alertBeans.get(key);
184         fired = (item.getAlertFired() == 1) | fired;
185         if(item.getAlertFired() == 1)
186         {
187           alertsFired += item._alertItemName;
188           alertsFired += ";";
189         }
190       }
191     }
192     Tuple<String> t = new Tuple<String>();
193     t.add("0");
194     AlertValueAndStatus summaryStatus = new AlertValueAndStatus(t, fired);
195     if(!_alertBeans.containsKey(summaryKey))
196     {
197       ClusterAlertSummary item = new ClusterAlertSummary(summaryKey, summaryStatus);
198       onNewAlertMbeanAdded(item);
199       item.setAdditionalInfo(alertsFired);
200       _alertBeans.put(summaryKey, item);
201       _clusterAlertSummary = item;
202     }
203     else
204     {
205       _alertBeans.get(summaryKey).setValueMap(summaryStatus);
206       _alertBeans.get(summaryKey).setAdditionalInfo(alertsFired);
207     }
208   }
209   
210   void register(Object bean, ObjectName name)
211   {
212     try
213     {
214       _beanServer.unregisterMBean(name);
215     }
216     catch (Exception e)
217     {
218     }
219     try
220     {
221       _beanServer.registerMBean(bean, name);
222     }
223     catch (Exception e)
224     {
225       _logger.error("Could not register MBean", e);
226     }
227   }
228   
229   public void reset()
230   {
231     for(String beanName : _alertBeans.keySet())
232     {
233       ClusterAlertItem item = _alertBeans.get(beanName);
234       item.reset();
235       try
236       {
237         ObjectName objectName =  new ObjectName(DOMAIN_ALERT+":alert="+item.getSensorName());
238         _beanServer.unregisterMBean(objectName);
239       }
240       catch (Exception e)
241       {
242         _logger.warn("", e);
243       }
244     }
245     _alertBeans.clear();
246   }
247 
248   public void refreshAlertDelta(String clusterName)
249   {
250     // Update the alert turn on/turn off history
251     String summaryKey = ALERT_SUMMARY + "_" + clusterName;
252     Set<String> currentFiredAlerts = new HashSet<String>();
253     for(String key : _alertBeans.keySet())
254     {
255       if(!key.equals(summaryKey))
256       {
257         ClusterAlertItem item = _alertBeans.get(key);
258         if(item.getAlertFired() == 1)
259         {
260           currentFiredAlerts.add(item._alertItemName);
261         }
262       }
263     }
264     
265     Map<String, String> onOffAlertsMap = new HashMap<String, String>();
266     for(String alertName : currentFiredAlerts)
267     {
268       if(!_previousFiredAlerts.contains(alertName))
269       {
270         onOffAlertsMap.put(alertName, "ON");
271         _logger.info(alertName + " ON");
272         _previousFiredAlerts.add(alertName);
273       }
274     }      
275     for(String cachedAlert : _previousFiredAlerts)
276     {
277       if(!currentFiredAlerts.contains(cachedAlert))
278       {
279         onOffAlertsMap.put(cachedAlert, "OFF");
280         _logger.info(cachedAlert + " OFF");
281       }
282     }
283     for(String key : onOffAlertsMap.keySet())
284     {
285       if(onOffAlertsMap.get(key).equals("OFF"))
286       {
287         _previousFiredAlerts.remove(key);
288       }
289     }
290     if(onOffAlertsMap.size() == 0)
291     {
292       _logger.info("No MBean change");
293     }
294     _recentAlertDelta = onOffAlertsMap;
295 
296     checkMBeanFreshness(ALERT_NOCHANGE_THRESHOLD);
297   }
298   
299   public Map<String, String> getRecentAlertDelta()
300   {
301     return _recentAlertDelta;
302   }
303   
304   /**
305    * Remove mbeans that has not been changed for thresholdInMs MS
306    * */
307   void checkMBeanFreshness(long thresholdInMs)
308   {
309     long now = new Date().getTime();
310     Set<String> oldBeanNames = new HashSet<String>();
311     // Get mbean items that has not been updated for thresholdInMs
312     for(String beanName : _alertBeans.keySet())
313     {
314       ClusterAlertItem item = _alertBeans.get(beanName);
315       if(now - item.getLastUpdateTime() > thresholdInMs)
316       {
317         oldBeanNames.add(beanName);
318         _logger.info("bean " + beanName+" has not been updated for "+ thresholdInMs +" MS");
319       }
320     }
321     for(String beanName : oldBeanNames)
322     {
323       ClusterAlertItem item = _alertBeans.get(beanName);
324       _alertBeans.remove(beanName);
325       try
326       {
327         item.reset();      
328         ObjectName objectName =  new ObjectName(DOMAIN_ALERT+":alert="+item.getSensorName());
329         _beanServer.unregisterMBean(objectName);
330       }
331       catch (Exception e)
332       {
333         _logger.warn("", e);
334       }
335     }
336   }
337 }