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