001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.monitors;
028    
029    
030    
031    import java.lang.management.GarbageCollectorMXBean;
032    import java.lang.management.ManagementFactory;
033    import java.lang.management.MemoryPoolMXBean;
034    import java.lang.management.MemoryUsage;
035    import java.util.ArrayList;
036    import java.util.HashMap;
037    import java.util.LinkedHashSet;
038    
039    import org.opends.server.admin.std.server.MemoryUsageMonitorProviderCfg;
040    import org.opends.server.api.MonitorProvider;
041    import org.opends.server.config.ConfigException;
042    import org.opends.server.core.DirectoryServer;
043    import org.opends.server.protocols.asn1.ASN1OctetString;
044    import org.opends.server.types.Attribute;
045    import org.opends.server.types.AttributeType;
046    import org.opends.server.types.AttributeValue;
047    import org.opends.server.types.InitializationException;
048    
049    
050    
051    /**
052     * This class defines a monitor provider that reports information about
053     * Directory Server memory usage.
054     */
055    public class MemoryUsageMonitorProvider
056           extends MonitorProvider<MemoryUsageMonitorProviderCfg>
057    {
058      // A map of the last GC counts seen by this monitor for calculating recent
059      // stats.
060      private HashMap<String,Long> lastGCCounts = new HashMap<String,Long>();
061    
062      // A map of the last GC times seen by this monitor for calculating recent
063      // stats.
064      private HashMap<String,Long> lastGCTimes = new HashMap<String,Long>();
065    
066      // A map of the most recent GC durations seen by this monitor.
067      private HashMap<String,Long> recentGCDurations = new HashMap<String,Long>();
068    
069      // A map of the memory manager names to names that are safe for use in
070      // attribute names.
071      private HashMap<String,String> gcSafeNames = new HashMap<String,String>();
072    
073    
074    
075      /**
076       * Initializes this monitor provider.
077       */
078      public MemoryUsageMonitorProvider()
079      {
080        super("JVM Memory Usage Monitor Provider");
081    
082        // No initialization should be performed here.
083      }
084    
085    
086    
087      /**
088       * {@inheritDoc}
089       */
090      public void initializeMonitorProvider(
091                       MemoryUsageMonitorProviderCfg configuration)
092             throws ConfigException, InitializationException
093      {
094        // No initialization is required.
095      }
096    
097    
098    
099      /**
100       * {@inheritDoc}
101       */
102      @Override()
103      public String getMonitorInstanceName()
104      {
105        return "JVM Memory Usage";
106      }
107    
108    
109    
110      /**
111       * {@inheritDoc}
112       */
113      @Override()
114      public long getUpdateInterval()
115      {
116        // Update the information once every second.
117        return 1000;
118      }
119    
120    
121    
122      /**
123       * {@inheritDoc}
124       */
125      @Override()
126      public void updateMonitorData()
127      {
128        for (GarbageCollectorMXBean gc :
129             ManagementFactory.getGarbageCollectorMXBeans())
130        {
131          String gcName  = gc.getName();
132          long   gcCount = gc.getCollectionCount();
133          long   gcTime  = gc.getCollectionTime();
134    
135          long lastGCCount      = 0L;
136          long lastGCTime       = 0L;
137          long recentGCDuration = 0L;
138          if (lastGCCounts.containsKey(gcName))
139          {
140            lastGCCount      = lastGCCounts.get(gcName);
141            lastGCTime       = lastGCTimes.get(gcName);
142            recentGCDuration = recentGCDurations.get(gcName);
143          }
144    
145          if (gcCount > lastGCCount)
146          {
147            long recentGCCount = gcCount - lastGCCount;
148            long recentGCTime  = gcTime  - lastGCTime;
149            recentGCDuration   = (recentGCTime / recentGCCount);
150          }
151    
152          lastGCCounts.put(gcName, gcCount);
153          lastGCTimes.put(gcName, gcTime);
154          recentGCDurations.put(gcName, recentGCDuration);
155        }
156      }
157    
158    
159    
160      /**
161       * {@inheritDoc}
162       */
163      @Override()
164      public ArrayList<Attribute> getMonitorData()
165      {
166        ArrayList<Attribute> attrs = new ArrayList<Attribute>();
167    
168        for (GarbageCollectorMXBean gc :
169             ManagementFactory.getGarbageCollectorMXBeans())
170        {
171          String gcName  = gc.getName();
172          long   gcCount = gc.getCollectionCount();
173          long   gcTime  = gc.getCollectionTime();
174    
175          long avgGCDuration = 0L;
176          if (gcCount > 0)
177          {
178            avgGCDuration = gcTime / gcCount;
179          }
180    
181          long recentGCDuration = 0L;
182          if (recentGCDurations.containsKey(gcName))
183          {
184            recentGCDuration = recentGCDurations.get(gcName);
185          }
186    
187          String safeName = gcSafeNames.get(gcName);
188          if (safeName == null)
189          {
190            safeName = generateSafeName(gcName);
191            gcSafeNames.put(gcName, safeName);
192          }
193    
194          attrs.add(createAttribute(safeName + "-total-collection-count",
195                                    String.valueOf(gcCount)));
196          attrs.add(createAttribute(safeName + "-total-collection-duration",
197                                    String.valueOf(gcTime)));
198          attrs.add(createAttribute(safeName + "-average-collection-duration",
199                                    String.valueOf(avgGCDuration)));
200          attrs.add(createAttribute(safeName + "-recent-collection-duration",
201                                    String.valueOf(recentGCDuration)));
202        }
203    
204        for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans())
205        {
206          String      poolName        = mp.getName();
207          MemoryUsage currentUsage    = mp.getUsage();
208          MemoryUsage collectionUsage = mp.getCollectionUsage();
209    
210          String safeName = gcSafeNames.get(poolName);
211          if (safeName == null)
212          {
213            safeName = generateSafeName(poolName);
214            gcSafeNames.put(poolName, safeName);
215          }
216    
217          if (currentUsage == null)
218          {
219            attrs.add(createAttribute(safeName + "-current-bytes-used", "0"));
220          }
221          else
222          {
223            attrs.add(createAttribute(safeName + "-current-bytes-used",
224                                      String.valueOf(currentUsage.getUsed())));
225          }
226    
227          if (collectionUsage == null)
228          {
229            attrs.add(createAttribute(safeName +
230                                           "-bytes-used-after-last-collection",
231                                      "0"));
232          }
233          else
234          {
235            attrs.add(createAttribute(safeName +
236                                           "-bytes-used-after-last-collection",
237                                      String.valueOf(collectionUsage.getUsed())));
238          }
239        }
240    
241        return attrs;
242      }
243    
244    
245    
246      /**
247       * Constructs an attribute using the provided information.  It will have the
248       * default syntax.
249       *
250       * @param  name   The name to use for the attribute.
251       * @param  value  The value to use for the attribute.
252       *
253       * @return  The attribute created from the provided information.
254       */
255      private Attribute createAttribute(String name, String value)
256      {
257        AttributeType attrType = DirectoryServer.getDefaultAttributeType(name);
258    
259        ASN1OctetString encodedValue = new ASN1OctetString(value);
260        LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(1);
261    
262        try
263        {
264          values.add(new AttributeValue(encodedValue,
265                                        attrType.normalize(encodedValue)));
266        }
267        catch (Exception e)
268        {
269          values.add(new AttributeValue(encodedValue, encodedValue));
270        }
271    
272        return new Attribute(attrType, name, values);
273      }
274    
275    
276    
277      /**
278       * Creates a "safe" version of the provided name, which is acceptable for
279       * use as part of an attribute name.
280       *
281       * @param  name  The name for which to obtain the safe name.
282       *
283       * @return  The calculated safe name.
284       */
285      private String generateSafeName(String name)
286      {
287        StringBuilder buffer = new StringBuilder();
288        boolean lastWasUppercase = false;
289        boolean lastWasDash      = false;
290        for (int i=0; i  < name.length(); i++)
291        {
292          char c = name.charAt(i);
293          if (Character.isLetter(c))
294          {
295            if (Character.isUpperCase(c))
296            {
297              char lowerCaseCharacter = Character.toLowerCase(c);
298              if ((buffer.length() > 0) && (! lastWasUppercase) && (! lastWasDash))
299              {
300                buffer.append('-');
301              }
302    
303              buffer.append(lowerCaseCharacter);
304              lastWasUppercase = true;
305              lastWasDash = false;
306            }
307            else
308            {
309              buffer.append(c);
310              lastWasUppercase = false;
311              lastWasDash = false;
312            }
313          }
314          else if (Character.isDigit(c))
315          {
316            buffer.append(c);
317            lastWasUppercase = false;
318            lastWasDash = false;
319          }
320          else if ((c == ' ') || (c == '_') || (c == '-'))
321          {
322            if (! lastWasDash)
323            {
324              buffer.append('-');
325            }
326    
327            lastWasUppercase = false;
328            lastWasDash = true;
329          }
330        }
331    
332        return buffer.toString();
333      }
334    }
335