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