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}