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