Project Report: fawkez

Packagesummary org.jcoderz.commons

org.jcoderz.commons.LoggableImpl

LineHitsNoteSource
1  /*
2   * $Id: LoggableImpl.java 1577 2009-12-07 15:44:44Z amandel $
3   *
4   * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5   *
6   * Redistribution and use in source and binary forms, with or without
7   * modification, are permitted provided that the following conditions are
8   * met:
9   *
10   *    * Redistributions of source code must retain the above copyright
11   *      notice, this list of conditions and the following disclaimer.
12   *    * Redistributions in binary form must reproduce the above
13   *      copyright notice, this list of conditions and the following
14   *      disclaimer in the documentation and/or other materials
15   *      provided with the distribution.
16   *    * Neither the name of the jCoderZ.org Project nor the names of
17   *      its contributors may be used to endorse or promote products
18   *      derived from this software without specific prior written
19   *      permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32   */
33  package org.jcoderz.commons;
34  
35  
36  import java.io.Serializable;
37  import java.net.InetAddress;
38  import java.net.UnknownHostException;
39  import java.util.ArrayList;
40  import java.util.Arrays;
41  import java.util.Collections;
42  import java.util.HashMap;
43  import java.util.Iterator;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Random;
47  import java.util.Set;
48  import java.util.Map.Entry;
49  import java.util.logging.Logger;
50  
51  import org.jcoderz.commons.util.ThrowableUtil;
52  
53  
54  /**
55   * Implements code common to all Exceptions.
56   * <p>
57   * The two base exceptions {@link org.jcoderz.commons.BaseException}
58   * and {@link org.jcoderz.commons.BaseRuntimeException} and the
59   * {@link org.jcoderz.commons.LogEvent} use a object of this class
60   * as a member and delegate all common calls to this member.
61   * </p>
62   * <p>
63   * This class also implements the special
64   * {@link org.jcoderz.commons.Loggable} that allows to add named
65   * parameters and to get a more detailed logging, with an assigned
66   * error message.
67   * </p>
68   * <p>
69   * The error response id is used to mark log entries with an unique id. This id
70   * is also returned to the client (caller). If the client reports the error
71   * response id it can be used to find a specific log entries more quickly. If
72   * the nested exception has already an error response id, it is re-used for this
73   * exception and will not get a new one.
74   * </p>
75   * Functionality provided by this class is:
76   * <ul>
77   * <li>Create a unique <code>ERROR_RESPONSE_ID</code> parameter for each
78   * instance.</li>
79   * <li>Create a message that contains all parameters for a full informational
80   * toString() output.</li>
81   * <li>Handles nested exceptions so that all information is available and
82   * avoids duplicate information for the JDK1.4 environment which supports nested
83   * exceptions itself.</li>
84   * <li>Holds the constant names for commonly used exception parameters.</li>
85   * </ul>
86   *
87   * @author Andreas Mandel
88   */
89  public class LoggableImpl
90        implements Serializable, Loggable
91  {
92     /** Name of this class. */
9375    public static final String CLASSNAME = LoggableImpl.class.getName();
94  
95     /** Logger used for this class. */
96100    public static final Logger logger = Logger.getLogger(CLASSNAME);
97  
98     /** Key used for the log message info parameter object. */
99     public static final String MESSAGE_INFO_PARAMETER_NAME = "_MESSAGE_INFO";
100  
101     /** Key used for the error response id added to the loggable. */
102     public static final String TRACKING_NUMBER_PARAMETER_NAME
103           = "_TRACKING_NUMBER";
104  
105     /** Key used for the root cause added to the loggable. */
106     public static final String CAUSE_PARAMETER_NAME = "_CAUSE";
107  
108     /** Key used for the thread id parameter object. */
109     public static final String THREAD_ID_PARAMETER_NAME = "_THREAD_ID";
110  
111     /** Key used for the thread name parameter object. */
112     public static final String THREAD_NAME_PARAMETER_NAME = "_THREAD_NAME";
113  
114     /** Key used for the instance id parameter object. */
115     public static final String INSTANCE_ID_PARAMETER_NAME = "_INSTANCE_ID";
116  
117     /** Key used for the node id parameter object. */
118     public static final String NODE_ID_PARAMETER_NAME = "_NODE_ID";
119  
120     /** Key used for the event time parameter of the loggable. */
121     public static final String EVENT_TIME_PARAMETER_NAME = "_TIME";
122  
123     /** Name of the application of the loggable. */
124     public static final String APPLICATION_NAME_PARAMETER_NAME = "_APPLICATION";
125  
126     /** Name of the group of the loggable. */
127     public static final String GROUP_NAME_PARAMETER_NAME = "_GROUP";
128  
129     /** Context parameter values prefix. */
130     public static final String CONTEXT_PARAMETER_PREFIX = "CTX~";
131     
132     /** This nodes id. */
133100    public static final String NODE_ID = getStaticNodeId();
134  
135     /** Id for this instance. */
136     public static final String INSTANCE_ID;
137  
138     /** Virtual thread Id generated for this thread. */
139100    public static final ThreadIdHolder THREAD_ID_GENERATOR
140         = new ThreadIdHolder();
141  
142     static final long serialVersionUID = 1;
143  
144     /**
145      * Maximum number of steps to get the cause of an exception,
146      * until we stop climbing up the cause chain.
147      */
148     private static final int MAX_EXCEPTION_CHAIN_UP = 20;
149  
150     /**
151      * In the first step use bea specific instance name, which is set as system
152      * property with the following name.
153 (1)    * TODO: Make this bea-independent, requires entry in logging.properties,
154      * system property, or something alike.
155      */
156     private static final String INSTANCE_NAME_PROPERTY = "weblogic.Name";
157  
158     /** Random generator to create pseudo unique Ids for each loggable. */
159100    private static final Random RANDOM_ID_GENERATOR = new Random();
160  
161  
162100    private static final String DUMMY_INSTANCE_ID
163           = "P" + Integer.toHexString(RANDOM_ID_GENERATOR.nextInt());
164     private static final String DUMMY_NODE_ID = "127.0.0.1";
165  
166     /**
167      * list of parameter for this exception The list is not thread save!
168      */
169100    private final Map mParameters = new HashMap();
170  
171     /**
172      * Remember the ERROR_RESPONSE_ID. Intention is to log this id with the
173      * exception and pass the Id to the recipient. It should be really easy to
174      * find the exception in the log.
175      */
176     private final String mTrackingNumber;
177  
178     /** The error ID for this loggable */
179     private final LogMessageInfo mLogMessageInfo;
180  
181     /** The point in time when this event occurred. */
182     private final long mEventTime;
183  
184     /** The node id. */
185     private final String mNodeId;
186  
187     /** The id for this instance id. */
188     private final String mInstanceId;
189  
190     /** The thread id. */
191     private final long mThreadId;
192  
193     /** The thread name. */
194     private final String mThreadName;
195  
196     /**
197      * The Throwable that caused this loggable.
198      * Should be equal to mOuter.getCause()
199      */
200     private Throwable mCause;
201  
202     /** The outer exception, where this loggable belongs to. */
203     private Loggable mOuter;
204  
205100    private String mClassName = null;
206100    private String mMethodName = null;
207  
208     static
209     {
210100       INSTANCE_ID = getStaticInstanceId();
211100    }
212  
213     /**
214      * Create this loggable provide the 'Loggable' functionality for the
215      * given outer loggable.
216      * @param outer the the outer loggable.
217      * @param errorId the static LogMessageInfo for this Loggable.
218      */
219     public LoggableImpl (Loggable outer, LogMessageInfo errorId)
220     {
221100       this(outer, errorId, THREAD_ID_GENERATOR.getThreadId(),
222            Thread.currentThread().getName(), INSTANCE_ID, NODE_ID);
223100    }
224  
225     /**
226      * Create this loggable provide the 'Loggable' functionality for the
227      * given outer loggable with an initial cause.
228      * @param outer the the outer loggable.
229      * @param errorId the static LogMessageInfo for this Loggable.
230      * @param cause the cause of the outer.
231      */
232     public LoggableImpl (Loggable outer, LogMessageInfo errorId,
233         Throwable cause)
234     {
235100       this(outer, errorId, THREAD_ID_GENERATOR.getThreadId(),
236            Thread.currentThread().getName(), INSTANCE_ID,
237              NODE_ID, cause);
238100    }
239  
240     /**
241      * Create this loggable provide the 'Loggable' functionality for the
242      * given outer loggable with the given dynamic parameters.
243      * @param outer the the outer loggable.
244      * @param errorId the static LogMessageInfo for this Loggable.
245      * @param threadId the threadId to be set.
246      * @param threadName the threadName to be set.
247      * @param instanceId the instanceId to be set.
248      * @param nodeId the nodeId to be set.
249      */
250     public LoggableImpl (Loggable outer, LogMessageInfo errorId,
251         long threadId, String threadName, String instanceId, String nodeId)
252100    {
253100       mEventTime = System.currentTimeMillis();
254100       mTrackingNumber = Integer.toHexString(RANDOM_ID_GENERATOR.nextInt());
255100       mLogMessageInfo = errorId;
256100       mThreadId = threadId;
257100       mThreadName = threadName;
258100       mInstanceId = instanceId;
259100       mNodeId = nodeId;
260100       mOuter = outer;
261100       initInternalParameters();
262100       initThreadContextParameters();
263100    }
264  
265     /**
266      * Create this loggable provide the 'Loggable' functionality for the
267      * given outer loggable with the given dynamic parameters and an
268      * initial cause..
269      * @param outer the the outer loggable.
270      * @param errorId the static LogMessageInfo for this Loggable.
271      * @param threadId the threadId to be set.
272      * @param threadName the threadName to be set.
273      * @param instanceId the instanceId to be set.
274      * @param nodeId the nodeId to be set.
275      * @param cause the cause of the outer.
276      */
277     public LoggableImpl (Loggable outer, LogMessageInfo errorId,
278         long threadId, String threadName, String instanceId, String nodeId,
279         Throwable cause)
280100    {
281100       mEventTime = System.currentTimeMillis();
282100       ThrowableUtil.fixChaining(cause);
283100       Throwable thr = cause;
284100       int depth = 0;
285        while (thr != null
286            && !(thr instanceof Loggable)
287100           && depth < MAX_EXCEPTION_CHAIN_UP)
288        {
289100           thr = thr.getCause();
290100           depth++;
291        }
292100       if (thr instanceof Loggable)
293        {
294100          mTrackingNumber = ((Loggable) thr).getTrackingNumber();
295        }
296        else
297        {
298100          mTrackingNumber = Integer.toHexString(RANDOM_ID_GENERATOR.nextInt());
299        }
300100       mLogMessageInfo = errorId;
301100       mThreadId = threadId;
302100       mThreadName = threadName;
303100       mInstanceId = instanceId;
304100       mNodeId = nodeId;
305100       mOuter = outer;
306100       initCause(cause);
307100       initInternalParameters();
308100       initThreadContextParameters();
309100    }
310  
311     /**
312      * Sets the cause of this throwable.
313      *
314      * This method should be called after the call to the
315      * {@link Throwable#initCause(Throwable)} for the case
316      * the super call fails.
317      *
318      * @param cause the cause of this Exception.
319      */
320     public final void initCause (Throwable cause)
321     {
322100       mCause = cause;
323100       addParameter(CAUSE_PARAMETER_NAME, cause);
324100       ThrowableUtil.fixChaining(cause);
325100       ThrowableUtil.collectNestedData(this);
326100    }
327  
328     /**
329      * Adds a new named parameter. The parameter is added at the end of the list
330      * of parameters. The same <code>name</code> might occur several times.
331      *
332      * @param name the name of the parameter.
333      * @param value The value of the parameter
334      */
335     public final void addParameter (String name, Serializable value)
336     {
337100       List values = (List) mParameters.get(name);
338100       if (values == null)
339        {
340100          values = new ArrayList();
341100          mParameters.put(name, values);
342        }
343100       values.add(value);
344100    }
345  
346     /** {@inheritDoc} */
347     public List getParameter (String name)
348     {
349100       final List values = (List) mParameters.get(name);
350  
351        final List result;
352100       if (values != null)
353        {
354100          result = Collections.unmodifiableList(values);
355        }
356        else
357        {
3580          result = Collections.EMPTY_LIST;
359        }
360100       return result;
361     }
362  
363     /** {@inheritDoc} */
364     public Set getParameterNames ()
365     {
366100       return Collections.unmodifiableSet(mParameters.keySet());
367     }
368  
369     /** {@inheritDoc} */
370     public final LogMessageInfo getLogMessageInfo ()
371     {
372100       return mLogMessageInfo;
373     }
374  
375     /** {@inheritDoc} */
376     public final String getTrackingNumber ()
377     {
378100       return mTrackingNumber;
379     }
380  
381     /** {@inheritDoc} */
382     public final long getEventTime ()
383     {
384100       return mEventTime;
385     }
386  
387     /** {@inheritDoc} */
388     public final String getNodeId ()
389     {
390100       return mNodeId;
391     }
392  
393     /** {@inheritDoc} */
394     public final String getInstanceId ()
395     {
396100       return mInstanceId;
397     }
398  
399     /** {@inheritDoc} */
400     public final long getThreadId ()
401     {
402100       return mThreadId;
403     }
404  
405     /** {@inheritDoc} */
406     public final String getThreadName ()
407     {
408100       return mThreadName;
409     }
410  
411     /** {@inheritDoc} */
412     public Throwable getCause ()
413     {
414100       return mCause;
415     }
416  
417     /** {@inheritDoc} */
418     public void log ()
419     {
420100       getSource();
421100       logger.logp(getLogMessageInfo().getLogLevel(), mClassName, mMethodName,
422              getMessage(), mOuter);
423100    }
424  
425     /** {@inheritDoc} */
426     public String getMessage ()
427     {
428100       return getLogMessageInfo().formatMessage(
429            mParameters, new StringBuffer()).toString();
430     }
431  
432     /** {@inheritDoc} */
433     public String getSourceClass ()
434     {
435100        getSource();
436100        return mClassName;
437     }
438  
439     /** {@inheritDoc} */
440     public String getSourceMethod ()
441     {
442100        getSource();
443100        return mMethodName;
444     }
445  
446     /** {@inheritDoc} */
447     public String toString ()
448     {
449100       final StringBuffer sb = new StringBuffer();
450100       if (mOuter != null)
451        {
452100           sb.append(mOuter.getClass().getName());
453        }
454        else
455        {
456100           sb.append(getClass().getName());
457        }
458100       sb.append(": ");
459100       getLogMessageInfo().formatMessage(mParameters, sb);
460100       return sb.toString();
461     }
462  
463     /** {@inheritDoc} */
464     public String toDetailedString ()
465     {
466100        final StringBuffer sb = new StringBuffer();
467100        LoggableImpl.appendParameters(sb, this);
468100        Throwable cause = null;
469100        if (mOuter != null)
470         {
4710            cause = mOuter.getCause();
472         }
473100        if (cause == null)
474         {
475100            cause = getCause();
476         }
477         // add parameters of nested chain
478100        int depth = 0;
479100        while (cause != null && depth < MAX_EXCEPTION_CHAIN_UP)
480         {
481100            if (cause instanceof Loggable)
482             {
483100                sb.append("\nCaused by: ");
484100                LoggableImpl.appendParameters(sb, (Loggable) cause);
485100                break;
486             }
487100            cause = cause.getCause();
488100            depth++;
489         }
490100        cause = null;
491100        if (mOuter != null)
492         {
4930            cause = mOuter.getCause();
494         }
495100        if (cause == null)
496         {
497100            cause = getCause();
498         }
499100        if (cause != null)
500         {
501100            sb.append('\n');
502100            sb.append(ThrowableUtil.toString(cause));
503         }
504100        return sb.toString();
505     }
506  
507     private void initInternalParameters ()
508     {
509100       addParameter(MESSAGE_INFO_PARAMETER_NAME, mLogMessageInfo);
510100       addParameter(TRACKING_NUMBER_PARAMETER_NAME, mTrackingNumber);
511100       addParameter(EVENT_TIME_PARAMETER_NAME, new Long(mEventTime));
512100       addParameter(THREAD_ID_PARAMETER_NAME, new Long(mThreadId));
513100       addParameter(THREAD_NAME_PARAMETER_NAME, mThreadName);
514100       addParameter(INSTANCE_ID_PARAMETER_NAME, mInstanceId);
515100       addParameter(NODE_ID_PARAMETER_NAME, mNodeId);
516100       addParameter(APPLICATION_NAME_PARAMETER_NAME,
517              mLogMessageInfo.getAppName());
518100       addParameter(GROUP_NAME_PARAMETER_NAME, mLogMessageInfo.getGroupName());
519100    }
520  
521     private final void initThreadContextParameters ()
522     {
523100        final Iterator i = LogThreadContext.get().entrySet().iterator();
524100        while (i.hasNext())
525         {
5260            final Entry entry = (Entry) i.next();
5270            addParameter(
528                 CONTEXT_PARAMETER_PREFIX + entry.getKey(),
529                 String.valueOf(entry.getValue()));
5300        }
531100    }
532     
533     private final void getSource ()
534     {
535        // not analyzed yet.
536100       if (mMethodName == null || mClassName == null)
537        {
538100           final StackTraceElement[] stack = new Throwable().getStackTrace();
539            // First, search back to a method in the Logger class.
540100           int ix = 0;
541100           boolean found = false;
542100           while (ix < stack.length)
543            {
544100              final StackTraceElement frame = stack[ix];
545100              final String cname = frame.getClassName();
546100              if (cname.equals(CLASSNAME))
547               {
548100                 found = true;
549               }
550100              else if (found)
551               {
552100                 break;
553               }
554100              ix++;
555100           }
556            // Now search for the first frame before the "LoggableImpl" class or
557            // LogMessageInfo class.
558100           while (ix < stack.length)
559            {
560100              final StackTraceElement frame = stack[ix];
561               try
562               {
563100                 final String cname = frame.getClassName();
564100                 final Class clazz = Class.forName(cname);
565100                 if (! (Loggable.class.isAssignableFrom(clazz)
566                        || LogMessageInfo.class.isAssignableFrom(clazz)))
567                  {
568                     // We've found the relevant frame.
569100                    setMethodAndClass(frame);
570100                    break;
571                  }
572               }
5730              catch (ClassNotFoundException e)
574               {
5750                 setMethodAndClass(frame);
5760                 break;
577100              }
578100              ix++;
579100           }
580        }
581100    }
582  
583     private void setMethodAndClass (final StackTraceElement frame)
584     {
585100       mClassName = frame.getClassName();
586100       mMethodName = frame.getMethodName();
587100       final String fileName = frame.getFileName();
588100       if (fileName != null)
589        {
590100          final int lineNumber = frame.getLineNumber();
591100          if (lineNumber >= 0)
592           {
593100             mMethodName = frame.getMethodName()
594                    + "(" + fileName + ":" + lineNumber + ")";
595           }
596           else
597           {
5980             mMethodName = frame.getMethodName() + "(" + fileName + ")";
599           }
600100       }
6010       else if (frame.getMethodName().indexOf('(') < 0)
602        {
6030          mMethodName = frame.getMethodName() + "()";
604        }
605        else
606        {
6070           mMethodName = frame.getMethodName();
608        }
609100    }
610  
611     private static void appendParameters (StringBuffer sb, Loggable loggable)
612     {
613100        sb.append(loggable.toString());
614  
615100        final Object[] params = loggable.getParameterNames().toArray();
616100        Arrays.sort(params);
617100        final Iterator/*<String>*/ parameterNames
618             = Arrays.asList(params).iterator();
619100        while (parameterNames.hasNext())
620         {
621100            final String parameterName = (String) parameterNames.next();
622100            sb.append("\n\t");
623100            sb.append(parameterName);
624100            sb.append(": \t");
625100            sb.append(loggable.getParameter(parameterName));
626100        }
627100    }
628  
629     private static String getStaticNodeId ()
630     {
631100       String nodeId = DUMMY_NODE_ID;
632        try
633        {
634100          nodeId = InetAddress.getLocalHost().getHostAddress();
635        }
6360       catch (UnknownHostException e)
637        {
6380(2)         System.err.println("Error retrieving inet address of local host, "
639                 + "setting " + DUMMY_NODE_ID + " as node id");
640100       }
641100       return nodeId;
642     }
643  
644     private static String getStaticInstanceId ()
645     {
646100       return System.getProperty(INSTANCE_NAME_PROPERTY, DUMMY_INSTANCE_ID);
647     }
648  
649  
650100    private static class ThreadIdHolder
651           extends ThreadLocal
652     {
653        private static final long INITIAL_THREAD_ID = 10L;
654100       private static long sNextThreadId = INITIAL_THREAD_ID;
655  
656        protected Object initialValue ()
657        {
658100          return new Long(sNextThreadId++);
659        }
660  
661        long getThreadId ()
662        {
663100          return ((Long) get()).longValue();
664        }
665     }
666  }

Findings in this File

f (3) Class org.jcoderz.commons.LoggableImpl defines non-transient non-serializable instance field mParameters Map implementation is assumed to be always serializable.
i (1) 153 : 0 Comment matches to-do format '(TODO|FIXME|CHECKME)'.
d (2) 638 : 10 System.out.print is used