001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.server.loggers.debug;
029    import org.opends.messages.Message;
030    
031    import org.opends.server.types.DebugLogLevel;
032    import org.opends.server.types.DebugLogCategory;
033    import org.opends.server.types.ConfigChangeResult;
034    import org.opends.server.types.ResultCode;
035    import org.opends.server.loggers.LogLevel;
036    import org.opends.server.loggers.LogCategory;
037    import org.opends.server.admin.server.ConfigurationChangeListener;
038    import org.opends.server.admin.std.meta.DebugTargetCfgDefn;
039    import org.opends.server.admin.std.server.DebugTargetCfg;
040    
041    
042    import java.util.Set;
043    import java.util.HashSet;
044    import java.util.ArrayList;
045    import java.util.List;
046    
047    /**
048     * This class encapsulates the trace settings in effect at a given traceing
049     * scope.
050     */
051    public class TraceSettings
052        implements ConfigurationChangeListener<DebugTargetCfg>
053    {
054      /** A TraceSettings object representing a fully disabled trace state. */
055      public static final TraceSettings DISABLED =
056          new TraceSettings(DebugLogLevel.DISABLED);
057    
058      private static final String STACK_DUMP_KEYWORD = "stack";
059      private static final String INCLUDE_CAUSE_KEYWORD = "cause";
060      private static final String SUPPRESS_ARG_KEYWORD = "noargs";
061      private static final String SUPPRESS_RETVAL_KEYWORD = "noretval";
062      private static final String INCLUDE_CATEGORY_KEYWORD = "category";
063      private static final String LEVEL_KEYWORD = "level";
064    
065      /**
066       * The log level of this setting.
067       */
068      LogLevel level;
069    
070      /**
071       * The log categories for this setting.
072       */
073      Set<LogCategory> includeCategories;
074    
075      /**
076       * Indicates if method arguments should be logged.
077       */
078      boolean noArgs;
079    
080      /**
081       * Indicates if method return values should be logged.
082       */
083      boolean noRetVal;
084    
085      /**
086       * The level of stack frames to include.
087       */
088      int stackDepth;
089    
090      /**
091       * Indicates if the cause exception is included in exception messages.
092       */
093      boolean includeCause;
094    
095      private DebugTargetCfg currentConfig;
096    
097      /**
098       * Construct new trace settings at the specified log level.
099       *
100       * @param level the log level for this setting.
101       */
102      public TraceSettings(LogLevel level)
103      {
104        this(level, null, false, false, 0, false);
105    
106      }
107    
108      /**
109       * Construct new trace settings at the specified log level and including
110       * the categories.
111       *
112       * @param level the log level for this setting.
113       * @param includeCategories the categories to include in this setting.
114       */
115      public TraceSettings(LogLevel level, Set<LogCategory> includeCategories)
116      {
117        this(level, includeCategories, false, false, 0, false);
118    
119      }
120    
121      /**
122       * Construct new trace settings at the specified log level and including
123       * the categories. Optionally turn off arguments and return value in entry
124       * and exit messages.
125       *
126       * @param level the log level for this setting.
127       * @param includeCategories the categories to include in this setting.
128       * @param noArgs whether to include arguments in the log messages.
129       * @param noRetVal whether to include return values in the log messages.
130       */
131      public TraceSettings(LogLevel level, Set<LogCategory> includeCategories,
132                           boolean noArgs, boolean noRetVal)
133      {
134        this(level, includeCategories, noArgs, noRetVal, 0, false);
135      }
136    
137      /**
138       * Construct new trace settings at the specified log level and including
139       * the categories. Optionally turn off arguments, return value in entry
140       * and exit messages, and specifying the depth of stack traces and whether
141       * to include the cause of exceptions.
142       *
143       * @param level the log level for this setting.
144       * @param includeCategories the categories to include in this setting.
145       * @param noArgs whether to include arguments in the log messages.
146       * @param noRetVal whether to include return values in the log messages.
147       * @param stackDepth the stack depth to display in log messages.
148       * @param includeCause whether to include the cause of exceptions.
149       */
150      public TraceSettings(LogLevel level, Set<LogCategory> includeCategories,
151                           boolean noArgs, boolean noRetVal, int stackDepth,
152                           boolean includeCause)
153      {
154        this.level = level;
155        this.includeCategories = includeCategories;
156        this.noArgs = noArgs;
157        this.noRetVal = noRetVal;
158        this.stackDepth = stackDepth;
159        this.includeCause = includeCause;
160      }
161    
162      /**
163       * Construct a new trace settings from the provided configuration.
164       *
165       * @param config The debug target configuration that contains the information
166       *               to use to initialize this trace setting.
167       */
168      public TraceSettings(DebugTargetCfg config)
169      {
170        this.level =
171            DebugLogLevel.parse(config.getDebugLevel().toString());
172    
173        Set<LogCategory> logCategories = null;
174        if(!config.getDebugCategory().isEmpty())
175        {
176          logCategories =
177              new HashSet<LogCategory>(config.getDebugCategory().size());
178          for(DebugTargetCfgDefn.DebugCategory category :
179              config.getDebugCategory())
180          {
181            logCategories.add(DebugLogCategory.parse(category.toString()));
182          }
183        }
184    
185        this.includeCategories = logCategories;
186        this.noArgs = config.isOmitMethodEntryArguments();
187        this.noRetVal = config.isOmitMethodReturnValue();
188        this.stackDepth = config.getThrowableStackFrames();
189        this.includeCause = config.isIncludeThrowableCause();
190    
191        currentConfig = config;
192        config.addChangeListener(this);
193      }
194    
195      /**
196       * {@inheritDoc}
197       */
198      public boolean isConfigurationChangeAcceptable(
199              DebugTargetCfg config,
200              List<Message> unacceptableReasons)
201      {
202        // This should alwas be acceptable. We are assuing that the scope for this
203        // trace setting is the same sine its part of the DN.
204        return true;
205      }
206    
207      /**
208       * {@inheritDoc}
209       */
210      public ConfigChangeResult applyConfigurationChange(DebugTargetCfg config)
211      {
212        // Default result code.
213        ResultCode resultCode = ResultCode.SUCCESS;
214        boolean adminActionRequired = false;
215        ArrayList<Message> messages = new ArrayList<Message>();
216    
217        // We can assume that the target scope did not change since its the
218        // naming attribute. Changing it would result in a modify DN.
219    
220        this.level =
221            DebugLogLevel.parse(config.getDebugLevel().toString());
222    
223        Set<LogCategory> logCategories = null;
224        if(!config.getDebugCategory().isEmpty())
225        {
226          logCategories =
227              new HashSet<LogCategory>(config.getDebugCategory().size());
228          for(DebugTargetCfgDefn.DebugCategory category :
229              config.getDebugCategory())
230          {
231            logCategories.add(DebugLogCategory.parse(category.toString()));
232          }
233        }
234    
235        this.includeCategories = logCategories;
236        this.noArgs = config.isOmitMethodEntryArguments();
237        this.noRetVal = config.isOmitMethodReturnValue();
238        this.stackDepth = config.getThrowableStackFrames();
239        this.includeCause = config.isIncludeThrowableCause();
240    
241        this.currentConfig = config;
242    
243        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
244      }
245    
246      /**
247       * Parse trace settings from the string representation.
248       *
249       * @param value the trace settings string to be parsed.
250       * @return the trace settings parsed from the string.
251       */
252      protected static TraceSettings parseTraceSettings(String value)
253      {
254        TraceSettings settings = null;
255        if(value != null)
256        {
257          //Touch DebugLogLevel and DebugLogCategory so they are statically
258          //initialized or parse will not see all the levels/categories.
259          LogLevel level = DebugLogLevel.ERROR;
260          LogCategory categoryStub = DebugLogCategory.MESSAGE;
261    
262          Set<LogCategory> includeCategories = null;
263          boolean noArgs = false;
264          boolean noRetVal = false;
265          int stackDepth = 0;
266          boolean includeCause = false;
267    
268          String[] keywords = value.split(",");
269    
270          for(String keyword : keywords)
271          {
272            //See if stack dump keyword is included
273            if(keyword.startsWith(STACK_DUMP_KEYWORD))
274            {
275              //See if a stack depth is included
276              if(keyword.length() == STACK_DUMP_KEYWORD.length())
277              {
278                stackDepth = DebugStackTraceFormatter.COMPLETE_STACK;
279              }
280              else
281              {
282                int depthStart= keyword.indexOf("=", STACK_DUMP_KEYWORD.length());
283                if (depthStart == STACK_DUMP_KEYWORD.length())
284                {
285                  try
286                  {
287                    stackDepth = Integer.valueOf(keyword.substring(depthStart+1));
288                  }
289                  catch(NumberFormatException nfe)
290                  { // TODO: i18n
291                    System.err.println("The keyword " + STACK_DUMP_KEYWORD +
292                        " contains an invalid depth value. The complete stack " +
293                        "will be included.");
294                  }
295                }
296              }
297            }
298            //See if to include cause in exception messages.
299            else if(keyword.equals(INCLUDE_CAUSE_KEYWORD))
300            {
301              includeCause = true;
302            }
303            //See if to supress method arguments.
304            else if(keyword.equals(SUPPRESS_ARG_KEYWORD))
305            {
306              noArgs = true;
307            }
308            //See if to supress return values.
309            else if(keyword.equals(SUPPRESS_RETVAL_KEYWORD))
310            {
311              noRetVal = true;
312            }
313            else if(keyword.startsWith(INCLUDE_CATEGORY_KEYWORD))
314            {
315              int categoryStart =
316                    keyword.indexOf("=", INCLUDE_CATEGORY_KEYWORD.length());
317    
318              if(keyword.length() == INCLUDE_CATEGORY_KEYWORD.length() ||
319                  categoryStart != INCLUDE_CATEGORY_KEYWORD.length())
320              { // TODO: i18n
321                System.err.println("The keyword " + INCLUDE_CATEGORY_KEYWORD +
322                    " does not contain an equal sign to define the set of " +
323                    "categories to include. All categories will be included.");
324              }
325              else
326              {
327                String[] categories =
328                    keyword.substring(categoryStart+1).split("[|]");
329                includeCategories = new HashSet<LogCategory>();
330                for(String category : categories)
331                {
332                  try
333                  {
334                    includeCategories.add(DebugLogCategory.parse(category));
335                  }
336                  catch(IllegalArgumentException iae)
337                  { // TODO: i18n
338                    System.err.println("The keyword " + INCLUDE_CATEGORY_KEYWORD +
339                        " contains an invalid debug log category: " +
340                        iae.toString() + ". It will be ignored.");
341                  }
342                }
343    
344              }
345            }
346            else if(keyword.startsWith(LEVEL_KEYWORD))
347            {
348              int levelStart =
349                    keyword.indexOf("=", LEVEL_KEYWORD.length());
350    
351              if(keyword.length() == LEVEL_KEYWORD.length() ||
352                  levelStart != LEVEL_KEYWORD.length())
353              { // TODO: i18n
354                System.err.println("The keyword " + LEVEL_KEYWORD +
355                    " does not contain an equal sign to specify the log level. " +
356                    "Default level of " + level.toString() + " will be used.");
357              }
358              else
359              {
360                try
361                {
362                  level = LogLevel.parse(keyword.substring(levelStart+1));
363                }
364                catch(IllegalArgumentException iae)
365                {  // TODO: i18n
366                  System.err.println("The keyword " + LEVEL_KEYWORD +
367                      " contains an invalid debug log level: " +
368                      iae.toString() + ". Default level of " + level.toString() +
369                      " will be used.");
370                }
371              }
372            }
373    
374          }
375          settings = new TraceSettings(level, includeCategories, noArgs, noRetVal,
376                                       stackDepth, includeCause);
377        }
378    
379        return settings;
380      }
381    }