001 /* 002 * Copyright 2008-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2015 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021 package com.unboundid.ldap.sdk.unboundidds.monitors; 022 023 024 025 import java.util.ArrayList; 026 import java.util.Arrays; 027 import java.util.Collections; 028 import java.util.LinkedHashMap; 029 import java.util.List; 030 import java.util.Map; 031 032 import com.unboundid.ldap.sdk.Attribute; 033 import com.unboundid.ldap.sdk.Entry; 034 import com.unboundid.util.NotMutable; 035 import com.unboundid.util.ThreadSafety; 036 import com.unboundid.util.ThreadSafetyLevel; 037 038 import static com.unboundid.ldap.sdk.unboundidds.monitors.MonitorMessages.*; 039 import static com.unboundid.util.Debug.*; 040 041 042 043 /** 044 * <BLOCKQUOTE> 045 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 046 * LDAP SDK for Java. It is not available for use in applications that 047 * include only the Standard Edition of the LDAP SDK, and is not supported for 048 * use in conjunction with non-UnboundID products. 049 * </BLOCKQUOTE> 050 * This class defines a monitor entry that provides access to the Directory 051 * Server stack trace information. The information that is available through 052 * this monitor is roughly the equivalent of what can be accessed using the 053 * {@link Thread#getAllStackTraces} method. See the {@link ThreadStackTrace} 054 * class for more information about what is available in each stack trace. 055 * <BR><BR> 056 * The server should present at most one stack trace monitor entry. It can be 057 * retrieved using the {@link MonitorManager#getStackTraceMonitorEntry} method. 058 * The {@link StackTraceMonitorEntry#getStackTraces} method can be used to 059 * retrieve the stack traces for each thread. Alternately, this information may 060 * be accessed using the generic API (although in this case, only the string 061 * representations of each stack trace frame are available). See the 062 * {@link MonitorManager} class documentation for an example that demonstrates 063 * the use of the generic API for accessing monitor data. 064 */ 065 @NotMutable() 066 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 067 public final class StackTraceMonitorEntry 068 extends MonitorEntry 069 { 070 /** 071 * The structural object class used in stack trace monitor entries. 072 */ 073 static final String STACK_TRACE_MONITOR_OC = 074 "ds-stack-trace-monitor-entry"; 075 076 077 078 /** 079 * The name of the attribute that contains the JVM stack trace for each 080 * thread. 081 */ 082 private static final String ATTR_JVM_STACK_TRACE = "jvmThread"; 083 084 085 086 /** 087 * The serial version UID for this serializable class. 088 */ 089 private static final long serialVersionUID = -9008690818438183908L; 090 091 092 093 // The list of thread stack traces. 094 private final List<ThreadStackTrace> stackTraces; 095 096 097 098 /** 099 * Creates a new stack trace monitor entry from the provided entry. 100 * 101 * @param entry The entry to be parsed as a stack trace monitor entry. 102 * It must not be {@code null}. 103 */ 104 public StackTraceMonitorEntry(final Entry entry) 105 { 106 super(entry); 107 108 final List<String> traceLines = getStrings(ATTR_JVM_STACK_TRACE); 109 if (traceLines.isEmpty()) 110 { 111 stackTraces = Collections.emptyList(); 112 } 113 else 114 { 115 final ArrayList<ThreadStackTrace> traces = 116 new ArrayList<ThreadStackTrace>(100); 117 118 try 119 { 120 int currentThreadID = -1; 121 String currentName = null; 122 ArrayList<StackTraceElement> currentElements = 123 new ArrayList<StackTraceElement>(20); 124 for (final String line : traceLines) 125 { 126 final int equalPos = line.indexOf('='); 127 final int spacePos = line.indexOf(' ', equalPos); 128 final int id = Integer.parseInt(line.substring(equalPos+1, spacePos)); 129 if (id != currentThreadID) 130 { 131 if (currentThreadID >= 0) 132 { 133 traces.add(new ThreadStackTrace(currentThreadID, currentName, 134 currentElements)); 135 } 136 137 currentThreadID = id; 138 currentElements = new ArrayList<StackTraceElement>(20); 139 140 final int dashesPos1 = line.indexOf("---------- ", spacePos); 141 final int dashesPos2 = line.indexOf(" ----------", dashesPos1); 142 currentName = line.substring((dashesPos1 + 11), dashesPos2); 143 } 144 else 145 { 146 final int bePos = line.indexOf("]="); 147 final String traceLine = line.substring(bePos+2); 148 149 final String fileName; 150 int lineNumber = -1; 151 final int closeParenPos = traceLine.lastIndexOf(')'); 152 final int openParenPos = traceLine.lastIndexOf('(', closeParenPos); 153 final int colonPos = traceLine.lastIndexOf(':', closeParenPos); 154 if (colonPos < 0) 155 { 156 fileName = traceLine.substring(openParenPos+1, closeParenPos); 157 } 158 else 159 { 160 fileName = traceLine.substring(openParenPos+1, colonPos); 161 162 final String lineNumberStr = 163 traceLine.substring(colonPos+1, closeParenPos); 164 if (lineNumberStr.equalsIgnoreCase("native")) 165 { 166 lineNumber = -2; 167 } 168 else 169 { 170 try 171 { 172 lineNumber = Integer.parseInt(lineNumberStr); 173 } catch (Exception e) {} 174 } 175 } 176 177 final int periodPos = traceLine.lastIndexOf('.', openParenPos); 178 final String className = traceLine.substring(0, periodPos); 179 final String methodName = 180 traceLine.substring(periodPos+1, openParenPos); 181 182 currentElements.add(new StackTraceElement(className, methodName, 183 fileName, lineNumber)); 184 } 185 } 186 187 if (currentThreadID >= 0) 188 { 189 traces.add(new ThreadStackTrace(currentThreadID, currentName, 190 currentElements)); 191 } 192 } 193 catch (Exception e) 194 { 195 debugException(e); 196 } 197 198 stackTraces = Collections.unmodifiableList(traces); 199 } 200 } 201 202 203 204 /** 205 * Retrieves the list of thread stack traces. 206 * 207 * @return The list of thread stack traces, or an empty list if it was not 208 * included in the monitor entry or a problem occurs while decoding 209 * the stack traces. 210 */ 211 public List<ThreadStackTrace> getStackTraces() 212 { 213 return stackTraces; 214 } 215 216 217 218 /** 219 * {@inheritDoc} 220 */ 221 @Override() 222 public String getMonitorDisplayName() 223 { 224 return INFO_STACK_TRACE_MONITOR_DISPNAME.get(); 225 } 226 227 228 229 /** 230 * {@inheritDoc} 231 */ 232 @Override() 233 public String getMonitorDescription() 234 { 235 return INFO_STACK_TRACE_MONITOR_DESC.get(); 236 } 237 238 239 240 /** 241 * {@inheritDoc} 242 */ 243 @Override() 244 public Map<String,MonitorAttribute> getMonitorAttributes() 245 { 246 final LinkedHashMap<String,MonitorAttribute> attrs = 247 new LinkedHashMap<String,MonitorAttribute>(); 248 249 final Attribute traceAttr = getEntry().getAttribute(ATTR_JVM_STACK_TRACE); 250 if (traceAttr != null) 251 { 252 addMonitorAttribute(attrs, 253 ATTR_JVM_STACK_TRACE, 254 INFO_STACK_TRACE_DISPNAME_TRACE.get(), 255 INFO_STACK_TRACE_DESC_TRACE.get(), 256 Collections.unmodifiableList(Arrays.asList(traceAttr.getValues()))); 257 } 258 259 return Collections.unmodifiableMap(attrs); 260 } 261 }