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