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 }