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