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.api.MonitorData;
030import org.forgerock.opendj.server.config.server.MemoryUsageMonitorProviderCfg;
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  @Override
054  public void initializeMonitorProvider(
055                   MemoryUsageMonitorProviderCfg configuration)
056         throws ConfigException, InitializationException
057  {
058    scheduleUpdate(this, 0, 1, TimeUnit.SECONDS);
059  }
060
061  /** {@inheritDoc} */
062  @Override
063  public String getMonitorInstanceName()
064  {
065    return "JVM Memory Usage";
066  }
067
068
069  /** {@inheritDoc} */
070  @Override
071  public void run()
072  {
073    for (GarbageCollectorMXBean gc :
074         ManagementFactory.getGarbageCollectorMXBeans())
075    {
076      String gcName  = gc.getName();
077      long   gcCount = gc.getCollectionCount();
078      long   gcTime  = gc.getCollectionTime();
079
080      long lastGCCount      = 0L;
081      long lastGCTime       = 0L;
082      long recentGCDuration = 0L;
083      if (lastGCCounts.containsKey(gcName))
084      {
085        lastGCCount      = lastGCCounts.get(gcName);
086        lastGCTime       = lastGCTimes.get(gcName);
087        recentGCDuration = recentGCDurations.get(gcName);
088      }
089
090      if (gcCount > lastGCCount)
091      {
092        long recentGCCount = gcCount - lastGCCount;
093        long recentGCTime  = gcTime  - lastGCTime;
094        recentGCDuration   = recentGCTime / recentGCCount;
095      }
096
097      lastGCCounts.put(gcName, gcCount);
098      lastGCTimes.put(gcName, gcTime);
099      recentGCDurations.put(gcName, recentGCDuration);
100    }
101  }
102
103
104
105  @Override
106  public MonitorData getMonitorData()
107  {
108    MonitorData attrs = new MonitorData();
109
110    for (GarbageCollectorMXBean gc :
111         ManagementFactory.getGarbageCollectorMXBeans())
112    {
113      String gcName  = gc.getName();
114      long   gcCount = gc.getCollectionCount();
115      long   gcTime  = gc.getCollectionTime();
116
117      long avgGCDuration = 0L;
118      if (gcCount > 0)
119      {
120        avgGCDuration = gcTime / gcCount;
121      }
122
123      long recentGCDuration = 0L;
124      if (recentGCDurations.containsKey(gcName))
125      {
126        recentGCDuration = recentGCDurations.get(gcName);
127      }
128
129      String safeName = gcSafeNames.get(gcName);
130      if (safeName == null)
131      {
132        safeName = generateSafeName(gcName);
133        gcSafeNames.put(gcName, safeName);
134      }
135
136      attrs.add(safeName + "-total-collection-count", gcCount);
137      attrs.add(safeName + "-total-collection-duration", gcTime);
138      attrs.add(safeName + "-average-collection-duration", avgGCDuration);
139      attrs.add(safeName + "-recent-collection-duration", recentGCDuration);
140    }
141
142    for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans())
143    {
144      String      poolName        = mp.getName();
145      MemoryUsage currentUsage    = mp.getUsage();
146      MemoryUsage collectionUsage = mp.getCollectionUsage();
147
148      String safeName = gcSafeNames.get(poolName);
149      if (safeName == null)
150      {
151        safeName = generateSafeName(poolName);
152        gcSafeNames.put(poolName, safeName);
153      }
154
155      long currentBytesUsed = currentUsage != null ? currentUsage.getUsed() : 0;
156      attrs.add(safeName + "-current-bytes-used", currentBytesUsed);
157
158      long collectionBytesUsed = collectionUsage != null ? collectionUsage.getUsed() : 0;
159      attrs.add(safeName + "-bytes-used-after-last-collection", collectionBytesUsed);
160    }
161
162    return attrs;
163  }
164
165  /**
166   * Creates a "safe" version of the provided name, which is acceptable for
167   * use as part of an attribute name.
168   *
169   * @param  name  The name for which to obtain the safe name.
170   *
171   * @return  The calculated safe name.
172   */
173  private String generateSafeName(String name)
174  {
175    StringBuilder buffer = new StringBuilder();
176    boolean lastWasUppercase = false;
177    boolean lastWasDash      = false;
178    for (int i=0; i  < name.length(); i++)
179    {
180      char c = name.charAt(i);
181      if (Character.isLetter(c))
182      {
183        if (Character.isUpperCase(c))
184        {
185          char lowerCaseCharacter = Character.toLowerCase(c);
186          if (buffer.length() > 0 && !lastWasUppercase && !lastWasDash)
187          {
188            buffer.append('-');
189          }
190
191          buffer.append(lowerCaseCharacter);
192          lastWasUppercase = true;
193          lastWasDash = false;
194        }
195        else
196        {
197          buffer.append(c);
198          lastWasUppercase = false;
199          lastWasDash = false;
200        }
201      }
202      else if (Character.isDigit(c))
203      {
204        buffer.append(c);
205        lastWasUppercase = false;
206        lastWasDash = false;
207      }
208      else if (c == ' ' || c == '_' || c == '-')
209      {
210        if (! lastWasDash)
211        {
212          buffer.append('-');
213        }
214
215        lastWasUppercase = false;
216        lastWasDash = true;
217      }
218    }
219
220    return buffer.toString();
221  }
222}
223