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.Debug; 035import com.unboundid.util.NotMutable; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038 039import static com.unboundid.ldap.sdk.unboundidds.monitors.MonitorMessages.*; 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 054 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 055 * for proprietary functionality or for external specifications that are not 056 * considered stable or mature enough to be guaranteed to work in an 057 * interoperable way with 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 = new ArrayList<>(100); 120 121 try 122 { 123 int currentThreadID = -1; 124 String currentName = null; 125 ArrayList<StackTraceElement> currentElements = new ArrayList<>(20); 126 for (final String line : traceLines) 127 { 128 final int equalPos = line.indexOf('='); 129 final int spacePos = line.indexOf(' ', equalPos); 130 final int id = Integer.parseInt(line.substring(equalPos+1, spacePos)); 131 if (id != currentThreadID) 132 { 133 if (currentThreadID >= 0) 134 { 135 traces.add(new ThreadStackTrace(currentThreadID, currentName, 136 currentElements)); 137 } 138 139 currentThreadID = id; 140 currentElements = new ArrayList<>(20); 141 142 final int dashesPos1 = line.indexOf("---------- ", spacePos); 143 final int dashesPos2 = line.indexOf(" ----------", dashesPos1); 144 currentName = line.substring((dashesPos1 + 11), dashesPos2); 145 } 146 else 147 { 148 final int bePos = line.indexOf("]="); 149 final String traceLine = line.substring(bePos+2); 150 151 final String fileName; 152 int lineNumber = -1; 153 final int closeParenPos = traceLine.lastIndexOf(')'); 154 final int openParenPos = traceLine.lastIndexOf('(', closeParenPos); 155 final int colonPos = traceLine.lastIndexOf(':', closeParenPos); 156 if (colonPos < 0) 157 { 158 fileName = traceLine.substring(openParenPos+1, closeParenPos); 159 } 160 else 161 { 162 fileName = traceLine.substring(openParenPos+1, colonPos); 163 164 final String lineNumberStr = 165 traceLine.substring(colonPos+1, closeParenPos); 166 if (lineNumberStr.equalsIgnoreCase("native")) 167 { 168 lineNumber = -2; 169 } 170 else 171 { 172 try 173 { 174 lineNumber = Integer.parseInt(lineNumberStr); 175 } catch (final Exception e) {} 176 } 177 } 178 179 final int periodPos = traceLine.lastIndexOf('.', openParenPos); 180 final String className = traceLine.substring(0, periodPos); 181 final String methodName = 182 traceLine.substring(periodPos+1, openParenPos); 183 184 currentElements.add(new StackTraceElement(className, methodName, 185 fileName, lineNumber)); 186 } 187 } 188 189 if (currentThreadID >= 0) 190 { 191 traces.add(new ThreadStackTrace(currentThreadID, currentName, 192 currentElements)); 193 } 194 } 195 catch (final Exception e) 196 { 197 Debug.debugException(e); 198 } 199 200 stackTraces = Collections.unmodifiableList(traces); 201 } 202 } 203 204 205 206 /** 207 * Retrieves the list of thread stack traces. 208 * 209 * @return The list of thread stack traces, or an empty list if it was not 210 * included in the monitor entry or a problem occurs while decoding 211 * the stack traces. 212 */ 213 public List<ThreadStackTrace> getStackTraces() 214 { 215 return stackTraces; 216 } 217 218 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override() 224 public String getMonitorDisplayName() 225 { 226 return INFO_STACK_TRACE_MONITOR_DISPNAME.get(); 227 } 228 229 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override() 235 public String getMonitorDescription() 236 { 237 return INFO_STACK_TRACE_MONITOR_DESC.get(); 238 } 239 240 241 242 /** 243 * {@inheritDoc} 244 */ 245 @Override() 246 public Map<String,MonitorAttribute> getMonitorAttributes() 247 { 248 final LinkedHashMap<String,MonitorAttribute> attrs = new LinkedHashMap<>(1); 249 250 final Attribute traceAttr = getEntry().getAttribute(ATTR_JVM_STACK_TRACE); 251 if (traceAttr != null) 252 { 253 addMonitorAttribute(attrs, 254 ATTR_JVM_STACK_TRACE, 255 INFO_STACK_TRACE_DISPNAME_TRACE.get(), 256 INFO_STACK_TRACE_DESC_TRACE.get(), 257 Collections.unmodifiableList(Arrays.asList(traceAttr.getValues()))); 258 } 259 260 return Collections.unmodifiableMap(attrs); 261 } 262}