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