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