001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.monitors;
018
019
020
021import java.lang.management.GarbageCollectorMXBean;
022import java.lang.management.ManagementFactory;
023import java.lang.management.MemoryPoolMXBean;
024import java.lang.management.MemoryUsage;
025import java.util.HashMap;
026import java.util.concurrent.TimeUnit;
027
028import org.forgerock.opendj.config.server.ConfigException;
029import org.opends.server.admin.std.server.MemoryUsageMonitorProviderCfg;
030import org.opends.server.api.MonitorData;
031import org.opends.server.api.MonitorProvider;
032import org.opends.server.types.InitializationException;
033
034/**
035 * This class defines a monitor provider that reports information about
036 * Directory Server memory usage.
037 */
038public class MemoryUsageMonitorProvider
039       extends MonitorProvider<MemoryUsageMonitorProviderCfg>
040       implements Runnable
041{
042  /** A map of the last GC counts seen by this monitor for calculating recent stats. */
043  private HashMap<String,Long> lastGCCounts = new HashMap<>();
044  /** A map of the last GC times seen by this monitor for calculating recent stats. */
045  private HashMap<String,Long> lastGCTimes = new HashMap<>();
046  /** A map of the most recent GC durations seen by this monitor. */
047  private HashMap<String,Long> recentGCDurations = new HashMap<>();
048  /** A map of the memory manager names to names that are safe for use in attribute names. */
049  private HashMap<String,String> gcSafeNames = new HashMap<>();
050
051
052  /** {@inheritDoc} */
053  public void initializeMonitorProvider(
054                   MemoryUsageMonitorProviderCfg configuration)
055         throws ConfigException, InitializationException
056  {
057    scheduleUpdate(this, 0, 1, TimeUnit.SECONDS);
058  }
059
060  /** {@inheritDoc} */
061  @Override
062  public String getMonitorInstanceName()
063  {
064    return "JVM Memory Usage";
065  }
066
067
068  /** {@inheritDoc} */
069  public void run()
070  {
071    for (GarbageCollectorMXBean gc :
072         ManagementFactory.getGarbageCollectorMXBeans())
073    {
074      String gcName  = gc.getName();
075      long   gcCount = gc.getCollectionCount();
076      long   gcTime  = gc.getCollectionTime();
077
078      long lastGCCount      = 0L;
079      long lastGCTime       = 0L;
080      long recentGCDuration = 0L;
081      if (lastGCCounts.containsKey(gcName))
082      {
083        lastGCCount      = lastGCCounts.get(gcName);
084        lastGCTime       = lastGCTimes.get(gcName);
085        recentGCDuration = recentGCDurations.get(gcName);
086      }
087
088      if (gcCount > lastGCCount)
089      {
090        long recentGCCount = gcCount - lastGCCount;
091        long recentGCTime  = gcTime  - lastGCTime;
092        recentGCDuration   = recentGCTime / recentGCCount;
093      }
094
095      lastGCCounts.put(gcName, gcCount);
096      lastGCTimes.put(gcName, gcTime);
097      recentGCDurations.put(gcName, recentGCDuration);
098    }
099  }
100
101
102
103  @Override
104  public MonitorData getMonitorData()
105  {
106    MonitorData attrs = new MonitorData();
107
108    for (GarbageCollectorMXBean gc :
109         ManagementFactory.getGarbageCollectorMXBeans())
110    {
111      String gcName  = gc.getName();
112      long   gcCount = gc.getCollectionCount();
113      long   gcTime  = gc.getCollectionTime();
114
115      long avgGCDuration = 0L;
116      if (gcCount > 0)
117      {
118        avgGCDuration = gcTime / gcCount;
119      }
120
121      long recentGCDuration = 0L;
122      if (recentGCDurations.containsKey(gcName))
123      {
124        recentGCDuration = recentGCDurations.get(gcName);
125      }
126
127      String safeName = gcSafeNames.get(gcName);
128      if (safeName == null)
129      {
130        safeName = generateSafeName(gcName);
131        gcSafeNames.put(gcName, safeName);
132      }
133
134      attrs.add(safeName + "-total-collection-count", gcCount);
135      attrs.add(safeName + "-total-collection-duration", gcTime);
136      attrs.add(safeName + "-average-collection-duration", avgGCDuration);
137      attrs.add(safeName + "-recent-collection-duration", recentGCDuration);
138    }
139
140    for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans())
141    {
142      String      poolName        = mp.getName();
143      MemoryUsage currentUsage    = mp.getUsage();
144      MemoryUsage collectionUsage = mp.getCollectionUsage();
145
146      String safeName = gcSafeNames.get(poolName);
147      if (safeName == null)
148      {
149        safeName = generateSafeName(poolName);
150        gcSafeNames.put(poolName, safeName);
151      }
152
153      long currentBytesUsed = currentUsage != null ? currentUsage.getUsed() : 0;
154      attrs.add(safeName + "-current-bytes-used", currentBytesUsed);
155
156      long collectionBytesUsed = collectionUsage != null ? collectionUsage.getUsed() : 0;
157      attrs.add(safeName + "-bytes-used-after-last-collection", collectionBytesUsed);
158    }
159
160    return attrs;
161  }
162
163  /**
164   * Creates a "safe" version of the provided name, which is acceptable for
165   * use as part of an attribute name.
166   *
167   * @param  name  The name for which to obtain the safe name.
168   *
169   * @return  The calculated safe name.
170   */
171  private String generateSafeName(String name)
172  {
173    StringBuilder buffer = new StringBuilder();
174    boolean lastWasUppercase = false;
175    boolean lastWasDash      = false;
176    for (int i=0; i  < name.length(); i++)
177    {
178      char c = name.charAt(i);
179      if (Character.isLetter(c))
180      {
181        if (Character.isUpperCase(c))
182        {
183          char lowerCaseCharacter = Character.toLowerCase(c);
184          if (buffer.length() > 0 && !lastWasUppercase && !lastWasDash)
185          {
186            buffer.append('-');
187          }
188
189          buffer.append(lowerCaseCharacter);
190          lastWasUppercase = true;
191          lastWasDash = false;
192        }
193        else
194        {
195          buffer.append(c);
196          lastWasUppercase = false;
197          lastWasDash = false;
198        }
199      }
200      else if (Character.isDigit(c))
201      {
202        buffer.append(c);
203        lastWasUppercase = false;
204        lastWasDash = false;
205      }
206      else if (c == ' ' || c == '_' || c == '-')
207      {
208        if (! lastWasDash)
209        {
210          buffer.append('-');
211        }
212
213        lastWasUppercase = false;
214        lastWasDash = true;
215      }
216    }
217
218    return buffer.toString();
219  }
220}
221