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    }