Skip to content

Package: SeverityComparator

SeverityComparator

nameinstructionbranchcomplexitylinemethod
SeverityComparator()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
apply(Throwable)
M: 46 C: 0
0%
M: 14 C: 0
0%
M: 8 C: 0
0%
M: 13 C: 0
0%
M: 1 C: 0
0%
applyThenCompare(Throwable, Throwable)
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
compare(Level, Level)
M: 14 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
compare(LogRecord, LogRecord)
M: 52 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 12 C: 0
0%
M: 1 C: 0
0%
compare(long, long)
M: 14 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
compareThrowable(Throwable, Throwable)
M: 85 C: 0
0%
M: 32 C: 0
0%
M: 17 C: 0
0%
M: 19 C: 0
0%
M: 1 C: 0
0%
equals(Object)
M: 13 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getInstance()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
hashCode()
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
isNormal(Throwable)
M: 38 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
toString(Object, Object)
M: 11 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package org.eclipse.angus.mail.util.logging;
18:
19: import java.io.Serializable;
20: import java.util.Comparator;
21: import java.util.logging.Level;
22: import java.util.logging.LogRecord;
23:
24: /**
25: * Orders log records by level, thrown, sequence, and time.
26: *
27: * This comparator orders LogRecords by how severely each is attributed to
28: * failures in a program. The primary ordering is determined by the use of the
29: * logging API throughout a program by specifying a level to each log message.
30: * The secondary ordering is determined at runtime by the type of errors and
31: * exceptions generated by the program. The remaining ordering assumes that
32: * older log records are less severe than newer log records.
33: *
34: * <p>
35: * The following LogRecord properties determine severity ordering:
36: * <ol>
37: * <li> The natural comparison of the LogRecord
38: * {@linkplain Level#intValue level}.
39: * <li> The expected recovery order of {@linkplain LogRecord#getThrown() thrown}
40: * property of a LogRecord and its cause chain. This ordering is derived from
41: * the JLS 11.1.1. The Kinds of Exceptions and JLS 11.5 The Exception Hierarchy.
42: * This is performed by {@linkplain #apply(java.lang.Throwable) finding} the
43: * throwable that best describes the entire cause chain. Once a specific
44: * throwable of each chain is identified it is then ranked lowest to highest by
45: * the following rules:
46: *
47: * <ul>
48: * <li>All LogRecords with a {@code Throwable} defined as
49: * "{@link #isNormal(java.lang.Throwable) normal occurrence}".
50: * <li>All LogRecords that do not have a thrown object.
51: * <li>All checked exceptions. This is any class that is assignable to the
52: * {@code java.lang.Throwable} class and is not a
53: * {@code java.lang.RuntimeException} or a {@code java.lang.Error}.
54: * <li>All unchecked exceptions. This is all {@code java.lang.RuntimeException}
55: * objects.
56: * <li>All errors that indicate a serious problem. This is all
57: * {@code java.lang.Error} objects.
58: * </ul>
59: * <li> The natural comparison of the LogRecord
60: * {@linkplain LogRecord#getSequenceNumber() sequence}.
61: * <li> The natural comparison of the LogRecord
62: * {@linkplain LogRecord#getMillis() millis}.
63: * </ol>
64: *
65: * @author Jason Mehrens
66: * @since JavaMail 1.5.2
67: */
68: public class SeverityComparator implements Comparator<LogRecord>, Serializable {
69:
70: /**
71: * The generated serial version UID.
72: */
73: private static final long serialVersionUID = -2620442245251791965L;
74:
75: /**
76: * A single instance that is shared among the logging package.
77: * The field is declared as java.util.Comparator so
78: * WebappClassLoader.clearReferencesStaticFinal() method will ignore this
79: * field.
80: */
81: private static final Comparator<LogRecord> INSTANCE
82: = new SeverityComparator();
83:
84: /**
85: * A shared instance of a SeverityComparator. This is package private so the
86: * public API is not polluted with more methods.
87: *
88: * @return a shared instance of a SeverityComparator.
89: */
90: static SeverityComparator getInstance() {
91: return (SeverityComparator) INSTANCE;
92: }
93:
94: /**
95: * Creates a default {@code SeverityComparator}.
96: */
97: public SeverityComparator() {
98: //readResolve() is not implemented in case the comparator
99: //is the target of a synchronized block.
100: }
101:
102: /**
103: * Identifies a single throwable that best describes the given throwable and
104: * the entire {@linkplain Throwable#getCause() cause} chain. This method can
105: * be overridden to change the behavior of
106: * {@link #compare(java.util.logging.LogRecord, java.util.logging.LogRecord)}.
107: *
108: * @param chain the throwable or null.
109: * @return null if null was given, otherwise the throwable that best
110: * describes the entire chain.
111: * @see #isNormal(java.lang.Throwable)
112: */
113: public Throwable apply(final Throwable chain) {
114: //Matches the j.u.f.UnaryOperator<Throwable> interface.
115: int limit = 0;
116: Throwable root = chain;
117: Throwable high = null;
118: Throwable normal = null;
119:• for (Throwable cause = chain; cause != null; cause = cause.getCause()) {
120: root = cause; //Find the deepest cause.
121:
122: //Find the deepest normal occurrance.
123:• if (isNormal(cause)) {
124: normal = cause;
125: }
126:
127: //Find the deepest error that happened before a normal occurrance.
128:• if (normal == null && cause instanceof Error) {
129: high = cause;
130: }
131:
132: //Deal with excessive cause chains and cyclic throwables.
133:• if (++limit == (1 << 16)) {
134: break; //Give up.
135: }
136: }
137:• return high != null ? high : normal != null ? normal : root;
138: }
139:
140: /**
141: * {@link #apply(java.lang.Throwable) Reduces} each throwable chain argument
142: * then compare each throwable result.
143: *
144: * @param tc1 the first throwable chain or null.
145: * @param tc2 the second throwable chain or null.
146: * @return a negative integer, zero, or a positive integer as the first
147: * argument is less than, equal to, or greater than the second.
148: * @see #apply(java.lang.Throwable)
149: * @see #compareThrowable(java.lang.Throwable, java.lang.Throwable)
150: */
151: public final int applyThenCompare(Throwable tc1, Throwable tc2) {
152:• return tc1 == tc2 ? 0 : compareThrowable(apply(tc1), apply(tc2));
153: }
154:
155: /**
156: * Compares two throwable objects or null. This method does not
157: * {@link #apply(java.lang.Throwable) reduce} each argument before
158: * comparing. This is method can be overridden to change the behavior of
159: * {@linkplain #compare(LogRecord, LogRecord)}.
160: *
161: * @param t1 the first throwable or null.
162: * @param t2 the second throwable or null.
163: * @return a negative integer, zero, or a positive integer as the first
164: * argument is less than, equal to, or greater than the second.
165: * @see #isNormal(java.lang.Throwable)
166: */
167: public int compareThrowable(final Throwable t1, final Throwable t2) {
168:• if (t1 == t2) { //Reflexive test including null.
169: return 0;
170: } else {
171: //Only one or the other is null at this point.
172: //Force normal occurrence to be lower than null.
173:• if (t1 == null) {
174:• return isNormal(t2) ? 1 : -1;
175: } else {
176:• if (t2 == null) {
177:• return isNormal(t1) ? -1 : 1;
178: }
179: }
180:
181: //From this point on neither are null.
182: //Follow the shortcut if we can.
183:• if (t1.getClass() == t2.getClass()) {
184: return 0;
185: }
186:
187: //Ensure normal occurrence flow control is ordered low.
188:• if (isNormal(t1)) {
189:• return isNormal(t2) ? 0 : -1;
190: } else {
191:• if (isNormal(t2)) {
192: return 1;
193: }
194: }
195:
196: //Rank the two unidenticial throwables using the rules from
197: //JLS 11.1.1. The Kinds of Exceptions and
198: //JLS 11.5 The Exception Hierarchy.
199:• if (t1 instanceof Error) {
200:• return t2 instanceof Error ? 0 : 1;
201:• } else if (t1 instanceof RuntimeException) {
202:• return t2 instanceof Error ? -1
203:• : t2 instanceof RuntimeException ? 0 : 1;
204: } else {
205: return t2 instanceof Error
206:• || t2 instanceof RuntimeException ? -1 : 0;
207: }
208: }
209: }
210:
211: /**
212: * Compares two log records based on severity.
213: *
214: * @param o1 the first log record.
215: * @param o2 the second log record.
216: * @return a negative integer, zero, or a positive integer as the first
217: * argument is less than, equal to, or greater than the second.
218: * @throws NullPointerException if either argument is null.
219: */
220: @SuppressWarnings("override") //JDK-6954234
221: public int compare(final LogRecord o1, final LogRecord o2) {
222:• if (o1 == null || o2 == null) { //Don't allow null.
223: throw new NullPointerException(toString(o1, o2));
224: }
225:
226: /**
227: * LogRecords are mutable so a reflexive relationship test is a safety
228: * requirement.
229: */
230:• if (o1 == o2) {
231: return 0;
232: }
233:
234: int cmp = compare(o1.getLevel(), o2.getLevel());
235:• if (cmp == 0) {
236: cmp = applyThenCompare(o1.getThrown(), o2.getThrown());
237:• if (cmp == 0) {
238: cmp = compare(o1.getSequenceNumber(), o2.getSequenceNumber());
239:• if (cmp == 0) {
240: cmp = compare(o1.getMillis(), o2.getMillis());
241: }
242: }
243: }
244: return cmp;
245: }
246:
247: /**
248: * Determines if the given object is also a comparator and it imposes the
249: * same ordering as this comparator.
250: *
251: * @param o the reference object with which to compare.
252: * @return true if this object equal to the argument; false otherwise.
253: */
254: @Override
255: public boolean equals(final Object o) {
256:• return o == null ? false : o.getClass() == getClass();
257: }
258:
259: /**
260: * Returns a hash code value for the object.
261: *
262: * @return Returns a hash code value for the object.
263: */
264: @Override
265: public int hashCode() {
266: return 31 * getClass().hashCode();
267: }
268:
269: /**
270: * Determines if the given throwable instance is "normal occurrence". This
271: * is any checked or unchecked exception with 'Interrupt' in the class name
272: * or ancestral class name. Any {@code java.lang.ThreadDeath} object or
273: * subclasses.
274: *
275: * This method can be overridden to change the behavior of the
276: * {@linkplain #apply(java.lang.Throwable)} method.
277: *
278: * @param t a throwable or null.
279: * @return true the given throwable is a "normal occurrence".
280: */
281: public boolean isNormal(final Throwable t) {
282:• if (t == null) { //This is only needed when called directly.
283: return false;
284: }
285:
286: /**
287: * Use the class names to avoid loading more classes.
288: */
289: final Class<?> root = Throwable.class;
290: final Class<?> error = Error.class;
291:• for (Class<?> c = t.getClass(); c != root; c = c.getSuperclass()) {
292:• if (error.isAssignableFrom(c)) {
293:• if (c.getName().equals("java.lang.ThreadDeath")) {
294: return true;
295: }
296: } else {
297: //Interrupt, Interrupted or Interruption.
298:• if (c.getName().contains("Interrupt")) {
299: return true;
300: }
301: }
302: }
303: return false;
304: }
305:
306: /**
307: * Compare two level objects.
308: *
309: * @param a the first level.
310: * @param b the second level.
311: * @return a negative integer, zero, or a positive integer as the first
312: * argument is less than, equal to, or greater than the second.
313: */
314: private int compare(final Level a, final Level b) {
315:• return a == b ? 0 : compare(a.intValue(), b.intValue());
316: }
317:
318: /**
319: * Outline the message create string.
320: *
321: * @param o1 argument one.
322: * @param o2 argument two.
323: * @return the message string.
324: */
325: private static String toString(final Object o1, final Object o2) {
326: return o1 + ", " + o2;
327: }
328:
329: /**
330: * Compare two longs. Can be removed when JDK 1.7 is required.
331: *
332: * @param x the first long.
333: * @param y the second long.
334: * @return a negative integer, zero, or a positive integer as the first
335: * argument is less than, equal to, or greater than the second.
336: */
337: private int compare(final long x, final long y) {
338:• return x < y ? -1 : x > y ? 1 : 0;
339: }
340: }