From 624e2c4d5f616c6edb1c2105b7a87f471cd1f668 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Mon, 29 Jan 2024 16:52:25 -0600 Subject: [PATCH 01/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- doc/src/main/resources/docs/CHANGES.txt | 1 + .../angus/mail/util/logging/MailHandler.java | 519 +++++++++++------- .../mail/util/logging/MailHandlerTest.java | 340 +++++++----- 3 files changed, 546 insertions(+), 314 deletions(-) diff --git a/doc/src/main/resources/docs/CHANGES.txt b/doc/src/main/resources/docs/CHANGES.txt index 23a784e..3b76bea 100644 --- a/doc/src/main/resources/docs/CHANGES.txt +++ b/doc/src/main/resources/docs/CHANGES.txt @@ -16,6 +16,7 @@ The following bugs have been fixed in the 2.0.3 release. 107: java.io.UnsupportedEncodingException: en_US.iso885915 if charset is "en_US.iso885915" 110: WildFly support for MailHandler 116: MailHandler LogManger support for mail entries +123: MailHander should catch ServiceConfigurationError CHANGES IN THE 2.0.2 RELEASE ---------------------------- diff --git a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java index aeced38..f50ffa1 100644 --- a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java +++ b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java @@ -68,6 +68,7 @@ import java.util.Objects; import java.util.Properties; import java.util.ResourceBundle; +import java.util.ServiceConfigurationError; import java.util.logging.ErrorManager; import java.util.logging.Filter; import java.util.logging.Formatter; @@ -408,6 +409,10 @@ public class MailHandler extends Handler { * Min byte size for header data. Used for initial arrays sizing. */ private static final int MIN_HEADER_SIZE = 1024; + /** + * Default capacity for the log record storage. + */ + private static final int DEFAULT_CAPACITY = 1000; /** * Cache the off value. */ @@ -444,11 +449,16 @@ public class MailHandler extends Handler { * This must be less than the PUBLISH state. */ private static final Integer MUTEX_REPORT = -4; + /** + * The used for service configuration error reporting. + * This must be less than the REPORT state and less than linkage. + */ + private static final Integer MUTEX_SERVICE = -8; /** * The used for linkage error reporting. * This must be less than the REPORT state. */ - private static final Integer MUTEX_LINKAGE = -8; + private static final Integer MUTEX_LINKAGE = -16; /** * Used to turn off security checks. */ @@ -461,7 +471,7 @@ public class MailHandler extends Handler { /** * Holds all of the email server properties. */ - private Properties mailProps; + private Properties mailProps = new Properties(); /** * Holds the authenticator required to login to the email server. */ @@ -491,6 +501,7 @@ public class MailHandler extends Handler { * The maximum number of log records to format per email. * Used to roughly bound the size of an email. * Every time the capacity is reached, the handler will push. + * Capacity is zero while the handler is being constructed. * The capacity will be negative if this handler is closed. * Negative values are used to ensure all records are pushed. */ @@ -517,7 +528,7 @@ public class MailHandler extends Handler { * This is only required if an email must be sent prior to shutdown * or before the buffer is full. */ - private Level pushLevel; + private Level pushLevel = Level.OFF; /** * Holds the push filter for trigger conditions requiring an early push. * Only gets called if the given log record is greater than or equal @@ -530,10 +541,11 @@ public class MailHandler extends Handler { */ private volatile Filter filter; /** - * Holds the level for this handler. + * Holds the level for this handler. Default value must be OFF. * There is no way to un-seal the super handler. + * @see #init(java.util.Properties) */ - private volatile Level logLevel = Level.ALL; + private volatile Level logLevel = Level.OFF; /** * Holds the filters for each attachment. Filters are optional for * each attachment. This is declared volatile because this is treated as @@ -541,14 +553,14 @@ public class MailHandler extends Handler { * positive. */ @SuppressWarnings("VolatileArrayField") - private volatile Filter[] attachmentFilters; + private volatile Filter[] attachmentFilters = emptyFilterArray(); /** * Holds the encoding name for this handler. * There is no way to un-seal the super handler. */ private String encoding; /** - * Holds the entry and body filter for this handler. + * Holds the body formatter for this handler. * There is no way to un-seal the super handler. */ private Formatter formatter; @@ -558,14 +570,14 @@ public class MailHandler extends Handler { * getHead, format, and getTail methods are only called if one or more * log records pass through the attachment filters. */ - private Formatter[] attachmentFormatters; + private Formatter[] attachmentFormatters = emptyFormatterArray(); /** * Holds the formatters that create the file name for each attachment. * Each formatter must produce a non null and non empty name. * The final file name will be the concatenation of one getHead call, plus * all of the format calls, plus one getTail call. */ - private Formatter[] attachmentNames; + private Formatter[] attachmentNames = emptyFormatterArray(); /** * Used to override the content type for the body and set the content type * for each attachment. @@ -575,6 +587,7 @@ public class MailHandler extends Handler { * Holds the error manager for this handler. * There is no way to un-seal the super handler. */ + @SuppressWarnings("this-escape") private volatile ErrorManager errorManager = defaultErrorManager(); /** @@ -584,6 +597,7 @@ public class MailHandler extends Handler { * @throws SecurityException if a security manager exists and the * caller does not have LoggingPermission("control"). */ + @SuppressWarnings("this-escape") public MailHandler() { init((Properties) null); } @@ -593,10 +607,12 @@ public MailHandler() { * LogManager configuration properties but overrides the * LogManager capacity with the given capacity. * - * @param capacity of the internal buffer. + * @param capacity of the internal buffer. If less than one the default of + * 1000 is used. * @throws SecurityException if a security manager exists and the * caller does not have LoggingPermission("control"). */ + @SuppressWarnings("this-escape") public MailHandler(final int capacity) { init((Properties) null); setCapacity0(capacity); @@ -613,6 +629,7 @@ public MailHandler(final int capacity) { * @throws SecurityException if a security manager exists and the * caller does not have LoggingPermission("control"). */ + @SuppressWarnings("this-escape") public MailHandler(Properties props) { init(props); //Must pass null or original object } @@ -685,6 +702,8 @@ public void publish(final LogRecord record) { } } catch (final LinkageError JDK8152515) { reportLinkageError(JDK8152515, ErrorManager.WRITE_FAILURE); + } catch (final ServiceConfigurationError sce) { + reportConfigurationError(sce, ErrorManager.WRITE_FAILURE); } finally { releaseMutex(); } @@ -911,8 +930,8 @@ public void flush() { */ @Override public void close() { + checkAccess(); try { - checkAccess(); //Ensure setLevel works before clearing the buffer. Message msg = null; synchronized (this) { try { @@ -920,6 +939,7 @@ public void close() { } finally { //Change level after formatting. this.logLevel = Level.OFF; this.disabledLevel = null; //free reference + /** * The sign bit of the capacity is set to ensure that * records that have passed isLoggable, but have yet to be @@ -930,10 +950,10 @@ public void close() { this.capacity = -this.capacity; } + //Only need room for one record after closed //Ensure not inside a push. if (size == 0 && data.length != 1) { - this.data = new LogRecord[1]; - this.matched = new int[this.data.length]; + initLogRecords(1); } } } @@ -943,6 +963,8 @@ public void close() { } } catch (final LinkageError JDK8152515) { reportLinkageError(JDK8152515, ErrorManager.CLOSE_FAILURE); + } catch (final ServiceConfigurationError sce) { + reportConfigurationError(sce, ErrorManager.CLOSE_FAILURE); } } @@ -954,8 +976,10 @@ public void close() { * @see #setLevel(java.util.logging.Level) * @since Angus Mail 2.0.3 */ - public boolean isEnabled() { - return this.logLevel.intValue() != offValue; //Volatile read + public synchronized boolean isEnabled() { + //For a this-escape, capacity will be zero and level will be off. + //No need to check that construction completed. + return capacity > 0 && this.logLevel.intValue() != offValue; } /** @@ -971,30 +995,19 @@ public boolean isEnabled() { * @see #isEnabled() * @since Angus Mail 2.0.3 */ - public void setEnabled(final boolean enabled) { + public synchronized void setEnabled(final boolean enabled) { checkAccess(); - setEnabled0(enabled); - } - - /** - * Used to enable or disable this handler. - * - * Pushes any buffered records to the email server as normal priority. - * The internal buffer is then cleared. - * - * @param enabled true to enable and false to disable. - * @since Angus Mail 2.0.3 - */ - private synchronized void setEnabled0(final boolean enabled) { if (this.capacity > 0) { //handler is open - this.push(false, ErrorManager.FLUSH_FAILURE); + if (this.size != 0) { + push(false, ErrorManager.FLUSH_FAILURE); + } if (enabled) { - if (disabledLevel != null) { //was disabled + if (this.disabledLevel != null) { //was disabled this.logLevel = this.disabledLevel; this.disabledLevel = null; } } else { - if (disabledLevel == null) { + if (this.disabledLevel == null) { this.disabledLevel = this.logLevel; this.logLevel = Level.OFF; } @@ -1041,6 +1054,8 @@ public void setLevel(final Level newLevel) { */ @Override public Level getLevel() { + //For a this-escape, this value will be OFF. + //No need to check that construction completed. return logLevel; //Volatile access. } @@ -1134,6 +1149,8 @@ public void setFilter(final Filter newFilter) { */ @Override public synchronized String getEncoding() { + //For a this-escape, this value will be null. + //No need to check that construction completed. return this.encoding; } @@ -1304,8 +1321,8 @@ public final synchronized void setComparator(Comparator c) { * @return the capacity. */ public final synchronized int getCapacity() { - assert capacity != Integer.MIN_VALUE && capacity != 0 : capacity; - return Math.abs(capacity); + assert capacity != Integer.MIN_VALUE : capacity; + return capacity != 0 ? Math.abs(capacity) : DEFAULT_CAPACITY; } /** @@ -1314,15 +1331,16 @@ public final synchronized int getCapacity() { * Pushes any buffered records to the email server as normal priority. * The internal buffer is then cleared. * - * @param newCapacity the max number of records. + * @param newCapacity the max number of records. The default capacity of + * 1000 is used if the given capacity is less than one. * @throws SecurityException if a security manager exists and the caller * does not have LoggingPermission("control"). - * @throws IllegalArgumentException is the new capacity is less than one. * @throws IllegalStateException if called from inside a push. * @see #flush() * @since Angus Mail 2.0.3 */ public final synchronized void setCapacity(int newCapacity) { + checkAccess(); setCapacity0(newCapacity); } @@ -1429,6 +1447,7 @@ private void setAuthenticator0(final Authenticator auth) { * @throws IllegalStateException if called from inside a push. */ public final void setMailProperties(Properties props) { + checkAccess(); if (props == null) { final String p = getClass().getName(); props = parseProperties( @@ -1450,13 +1469,18 @@ public final void setMailProperties(Properties props) { * @since Angus Mail 2.0.3 */ private Properties copyOf(Properties props) { - Properties copy = (Properties) props.clone(); //Allow subclass - return Objects.requireNonNull(copy); //Broken subclass + //Allow subclasses however, check that clone contract was followed in + //that a non-null Properties object was returned. + //No need to perform reflexive test or exact class matching tests as + //that doesn't really cause any unexpected failures. + Properties copy = (Properties) props.clone(); + return Objects.requireNonNull(copy, + props.getClass().getName()); } /** - * A private hook to handle overrides when the public method is declared - * non final. See public method for details. + * A private hook to set and validate properties. + * See public method for details. * * @param props a safe properties object. * @return true if verification key was present. @@ -1464,7 +1488,6 @@ private Properties copyOf(Properties props) { */ private boolean setMailProperties0(Properties props) { Objects.requireNonNull(props); - checkAccess(); Session settings; synchronized (this) { if (isWriting) { @@ -1531,6 +1554,7 @@ public final Properties getMailProperties() { * @since Angus Mail 2.0.3 */ public final void setMailEntries(String entries) { + checkAccess(); if (entries == null) { final String p = getClass().getName(); entries = fromLogManager(p.concat(".mailEntries")); @@ -1930,6 +1954,7 @@ public synchronized final void setSubjectFormatter(final Formatter format) { */ @Override protected void reportError(String msg, Exception ex, int code) { + //For a this-escape, the error manager will be the default. try { if (msg != null) { errorManager.error(Level.SEVERE.getName() @@ -1937,19 +1962,31 @@ protected void reportError(String msg, Exception ex, int code) { } else { errorManager.error((String) null, ex, code); } - } catch (RuntimeException | LinkageError GLASSFISH_21258) { + } catch (final RuntimeException | LinkageError GLASSFISH_21258) { + if (ex != null && GLASSFISH_21258 != ex) { + GLASSFISH_21258.addSuppressed(ex); + } reportLinkageError(GLASSFISH_21258, code); + } catch (final ServiceConfigurationError sce) { + if (ex != null) { + sce.addSuppressed(ex); + } + reportConfigurationError(sce, code); } } /** * Checks logging permissions if this handler has been sealed. + * Otherwise, this will check that this object was fully constructed. + * * @throws SecurityException if a security manager exists and the caller * does not have {@code LoggingPermission("control")}. */ private void checkAccess() { if (sealed) { LogManagerProperties.checkLogManagerAccess(); + } else { + throw new SecurityException("this-escape"); } } @@ -1976,7 +2013,8 @@ final String contentTypeOf(CharSequence chunk) { assert in.markSupported() : in.getClass().getName(); return URLConnection.guessContentTypeFromStream(in); } catch (final IOException IOE) { - reportError(IOE.getMessage(), IOE, ErrorManager.FORMAT_FAILURE); + reportError("Unable to guess content type", + IOE, ErrorManager.FORMAT_FAILURE); } } return null; //text/plain @@ -2095,8 +2133,39 @@ private void reportError(Message msg, Exception ex, int code) { } catch (final Exception e) { reportError(toMsgString(e), ex, code); } - } catch (final LinkageError GLASSFISH_21258) { + } catch (LinkageError GLASSFISH_21258) { + if (ex != null) { + GLASSFISH_21258.addSuppressed(ex); + } reportLinkageError(GLASSFISH_21258, code); + } catch (ServiceConfigurationError sce) { + if (ex != null) { + sce.addSuppressed(ex); + } + reportConfigurationError(sce, code); + } + } + + private void reportConfigurationError(Throwable t, int code) { + final Integer idx = MUTEX.get(); + if (idx == null || idx > MUTEX_SERVICE) { + MUTEX.set(MUTEX_SERVICE); + try { + reportError("Unable to load dependencies", + new IllegalStateException(t), code); + } catch (RuntimeException | ServiceConfigurationError + | LinkageError e) { + if (t != null && e != t) { + e.addSuppressed(t); + } + reportLinkageError(e, code); + } finally { + if (idx != null) { + MUTEX.set(idx); + } else { + MUTEX.remove(); + } + } } } @@ -2109,21 +2178,18 @@ private void reportError(Message msg, Exception ex, int code) { * * @param le the linkage error or a RuntimeException. * @param code the ErrorManager code. - * @throws NullPointerException if error is null. * @since JavaMail 1.5.3 */ private void reportLinkageError(final Throwable le, final int code) { - if (le == null) { - throw new NullPointerException(String.valueOf(code)); - } - + assert le != null : code; final Integer idx = MUTEX.get(); if (idx == null || idx > MUTEX_LINKAGE) { MUTEX.set(MUTEX_LINKAGE); try { Thread.currentThread().getUncaughtExceptionHandler() .uncaughtException(Thread.currentThread(), le); - } catch (RuntimeException | LinkageError ignore) { + } catch (RuntimeException | ServiceConfigurationError + | LinkageError ignore) { } finally { if (idx != null) { MUTEX.set(idx); @@ -2223,23 +2289,25 @@ private String contentWithEncoding(String type, String encoding) { * @throws IllegalStateException if called from inside a push. */ private synchronized void setCapacity0(int newCapacity) { - checkAccess(); - if (newCapacity <= 0) { - newCapacity = 1000; - } - if (isWriting) { throw new IllegalStateException(); } + if (this.capacity == 0) { + return; + } + + if (newCapacity <= 0) { + newCapacity = DEFAULT_CAPACITY; + } + if (this.capacity < 0) { //If closed, remain closed. this.capacity = -newCapacity; } else { this.push(false, ErrorManager.FLUSH_FAILURE); this.capacity = newCapacity; - if (this.data != null && this.data.length > newCapacity) { - this.data = Arrays.copyOf(data, newCapacity, LogRecord[].class); - this.matched = Arrays.copyOf(matched, newCapacity); + if (this.data.length > newCapacity) { + initLogRecords(1); } } } @@ -2387,8 +2455,11 @@ private void grow() { newCapacity = capacity; } assert len != capacity : len; - this.data = Arrays.copyOf(data, newCapacity, LogRecord[].class); - this.matched = Arrays.copyOf(matched, newCapacity); + final LogRecord[] d = Arrays.copyOf(data, newCapacity, LogRecord[].class); + final int[] m = Arrays.copyOf(matched, newCapacity); + //Ensure both arrays are created before assigning. + this.data = d; + this.matched = m; } /** @@ -2402,21 +2473,18 @@ private void grow() { * @see #sealed */ private synchronized void init(final Properties props) { - assert this.errorManager != null; - final String p = getClass().getName(); - this.mailProps = new Properties(); //ensure non-null on exception - final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); - try { - this.contentTypes = FileTypeMap.getDefaultFileTypeMap(); - } finally { - getAndSetContextClassLoader(ccl); - } + initLogRecords(0); //Ensure non-null even on exception. + LogManagerProperties.checkLogManagerAccess(); + final String p = getClass().getName(); //Assign any custom error manager first so it can detect all failures. + assert this.errorManager != null; //default set before custom object initErrorManager(fromLogManager(p.concat(".errorManager"))); - initCapacity(fromLogManager(p.concat(".capacity"))); - initLevel(fromLogManager(p.concat(".level"))); - initEnabled(fromLogManager(p.concat(".enabled"))); + int cap = parseCapacity(fromLogManager(p.concat(".capacity"))); + Level lvl = parseLevel(fromLogManager(p.concat(".level"))); + boolean enabled = parseEnabled(fromLogManager(p.concat(".enabled"))); + initContentTypes(); + initFilter(fromLogManager(p.concat(".filter"))); this.auth = newAuthenticator(fromLogManager(p.concat(".authenticator"))); @@ -2433,11 +2501,11 @@ private synchronized void init(final Properties props) { initAttachmentFilters(fromLogManager(p.concat(".attachment.filters"))); initAttachmentNames(fromLogManager(p.concat(".attachment.names"))); - //Verification of all of the MailHandler properties starts here - //That means setting new object members goes above this comment. //Entries are always parsed to report any errors. Properties entries = parseProperties(fromLogManager(p.concat(".mailEntries"))); - sealed = true; + + //Any new handler object members should be set above this line + String verify = fromLogManager(p.concat(".verify")); boolean verified; if (props != null) { //Given properties do not fallback to log manager. @@ -2447,14 +2515,29 @@ private synchronized void init(final Properties props) { //.mailEntries should fallback to log manager when verify key not present. verified = setMailProperties0(entries); } else { - checkAccess(); verified = false; } - if (!verified && fromLogManager(p.concat(".verify")) != null) { - verifySettings(initSession()); + //Fallback to top level verify properties if needed. + if (!verified && verify != null) { + try { + verifySettings(initSession()); + } catch (final RuntimeException re) { + reportError("Unable to verify", re, ErrorManager.OPEN_FAILURE); + } catch (final ServiceConfigurationError sce) { + reportConfigurationError(sce, ErrorManager.OPEN_FAILURE); + } } intern(); //Show verify warnings first. + + //Mark the handler as fully constructed by setting these fields. + this.capacity = cap; + if (enabled) { + this.logLevel = lvl; + } else { + this.disabledLevel = lvl; + } + sealed = true; } /** @@ -2471,26 +2554,18 @@ private void intern() { Object canidate; Object result; final Map seen = new HashMap<>(); - try { - intern(seen, this.errorManager); - } catch (final SecurityException se) { - reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE); - } + intern(seen, this.errorManager); - try { - canidate = this.filter; - result = intern(seen, canidate); - if (result != canidate && result instanceof Filter) { - this.filter = (Filter) result; - } + canidate = this.filter; + result = intern(seen, canidate); + if (result != canidate && result instanceof Filter) { + this.filter = (Filter) result; + } - canidate = this.formatter; - result = intern(seen, canidate); - if (result != canidate && result instanceof Formatter) { - this.formatter = (Formatter) result; - } - } catch (final SecurityException se) { - reportError(se.getMessage(), se, ErrorManager.OPEN_FAILURE); + canidate = this.formatter; + result = intern(seen, canidate); + if (result != canidate && result instanceof Formatter) { + this.formatter = (Formatter) result; } canidate = this.subjectFormatter; @@ -2525,10 +2600,12 @@ private void intern() { } } } catch (final Exception skip) { - reportError(skip.getMessage(), skip, ErrorManager.OPEN_FAILURE); + reportError("Unable to deduplcate", skip, ErrorManager.OPEN_FAILURE); } catch (final LinkageError skip) { - reportError(skip.getMessage(), new InvocationTargetException(skip), + reportError("Unable to deduplcate", new InvocationTargetException(skip), ErrorManager.OPEN_FAILURE); + } catch (final ServiceConfigurationError skip) { + reportConfigurationError(skip, ErrorManager.OPEN_FAILURE); } } @@ -2664,7 +2741,7 @@ private void initAttachmentFilters(final String list) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError(Integer.toString(i), E, ErrorManager.OPEN_FAILURE); } } } @@ -2710,7 +2787,7 @@ private void initAttachmentFormaters(final String list) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError(Integer.toString(i), E, ErrorManager.OPEN_FAILURE); a[i] = createSimpleFormatter(); } } else { @@ -2750,7 +2827,7 @@ private void initAttachmentNames(final String list) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError(Integer.toString(i), E, ErrorManager.OPEN_FAILURE); } } else { a[i] = TailNameFormatter.of(toString(attachmentFormatters[i])); @@ -2787,9 +2864,11 @@ private Authenticator newAuthenticator(final String name) { | ClassCastException literalAuth) { a = DefaultAuthenticator.of(name); } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); - } catch (final LinkageError JDK8152515) { - reportLinkageError(JDK8152515, ErrorManager.OPEN_FAILURE); + reportError("Unable to create authenticator", + E, ErrorManager.OPEN_FAILURE); + } catch (final LinkageError GLASSFISH_21258) { + reportLinkageError(GLASSFISH_21258, + ErrorManager.OPEN_FAILURE); } } else { //Authenticator is installed to provide the user name. a = DefaultAuthenticator.of(name); @@ -2804,21 +2883,37 @@ private Authenticator newAuthenticator(final String name) { * @param nameOrNumber the level name or number. * @throws SecurityException if not allowed. */ - private void initLevel(final String nameOrNumber) { + private Level parseLevel(final String nameOrNumber) { assert Thread.holdsLock(this); assert disabledLevel == null : disabledLevel; + Level lvl = Level.WARNING; try { if (!isEmpty(nameOrNumber)) { - logLevel = Level.parse(nameOrNumber); - } else { - logLevel = Level.WARNING; + lvl = Level.parse(nameOrNumber); } } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); - logLevel = Level.WARNING; + reportError(nameOrNumber, RE, ErrorManager.OPEN_FAILURE); } + return lvl; + } + + /** + * Creates the internal collection to store log records. + * This method assumes that no log records are currently stored. + * + * @param records the number of records. + * @throws RuntimeException if records is negative. + * @since Angus Mail 2.0.3 + */ + private void initLogRecords(final int records) { + assert this.size == 0 : this.size; + final LogRecord[] d = new LogRecord[records]; + final int[] m = new int[records]; + //Ensure both arrays are created before assigning. + this.data = d; + this.matched = m; } /** @@ -2833,31 +2928,32 @@ private void initLevel(final String nameOrNumber) { * @see #setMailEntries(java.lang.String) */ private Properties parseProperties(String entries) { - if (entries != null) { - final Properties props = new Properties(); - if (!hasValue(entries)) { - return props; - } + if (entries == null) { + return null; + } - /** - * The characters # and ! are used for comment lines in properties - * format. The characters \r or \n are not allowed in WildFly form - * validation however, properties comment characters are allowed. - * Comment lines are useless for this handler therefore, "#!" - * characters are used to represent logical lines and are assumed to - * not be present together in a key or value. - */ - try { - entries = entries.replace("#!", "\r\n"); - //Dynamic cast used so byte code verifier doesn't load StringReader - props.load(Reader.class.cast(new StringReader(entries))); - } catch (IOException | RuntimeException ex) { - reportError(entries, ex, ErrorManager.OPEN_FAILURE); - //Allow a partial load of properties to be set - } - return props; + final Properties props = new Properties(); + if (!hasValue(entries)) { + return props; } - return null; + + /** + * The characters # and ! are used for comment lines in properties + * format. The characters \r or \n are not allowed in WildFly form + * validation however, properties comment characters are allowed. + * Comment lines are useless for this handler therefore, "#!" + * characters are used to represent logical lines and are assumed to + * not be present together in a key or value. + */ + try { + entries = entries.replace("#!", "\r\n"); + //Dynamic cast used so byte code verifier doesn't load StringReader + props.load(Reader.class.cast(new StringReader(entries))); + } catch (IOException | RuntimeException ex) { + reportError(entries, ex, ErrorManager.OPEN_FAILURE); + //Allow a partial load of properties to be set + } + return props; } /** @@ -2867,14 +2963,10 @@ private Properties parseProperties(String entries) { * @param enabled the string false will only disable this handler. * @since Angus Mail 2.0.3 */ - private void initEnabled(final String enabled) { + private boolean parseEnabled(final String enabled) { assert Thread.holdsLock(this); - assert logLevel != null; - assert capacity != 0; //By default the Handler is enabled so only need to disable it on init. - if (hasValue(enabled) && !Boolean.parseBoolean(enabled)) { - setEnabled0(false); - } + return !hasValue(enabled) || Boolean.parseBoolean(enabled); } /** @@ -2894,7 +2986,8 @@ private void initFilter(final String name) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError("Unable to create filter", + E, ErrorManager.OPEN_FAILURE); } } @@ -2904,27 +2997,23 @@ private void initFilter(final String name) { * @param value the capacity value. * @throws SecurityException if not allowed. */ - private void initCapacity(final String value) { + private int parseCapacity(final String value) { assert Thread.holdsLock(this); - final int DEFAULT_CAPACITY = 1000; + int cap = 0; try { if (value != null) { - this.setCapacity0(Integer.parseInt(value)); - } else { - this.setCapacity0(DEFAULT_CAPACITY); + cap = Integer.parseInt(value); } } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); + reportError("Unable to set capacity", RE, ErrorManager.OPEN_FAILURE); } - if (capacity <= 0) { - capacity = DEFAULT_CAPACITY; + if (cap <= 0) { + cap = DEFAULT_CAPACITY; } - - this.data = new LogRecord[1]; - this.matched = new int[this.data.length]; + return cap; } /** @@ -2940,7 +3029,7 @@ private void initEncoding(final String e) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (UnsupportedEncodingException | RuntimeException UEE) { - reportError(UEE.getMessage(), UEE, ErrorManager.OPEN_FAILURE); + reportError(e, UEE, ErrorManager.OPEN_FAILURE); } } @@ -2971,8 +3060,8 @@ private ErrorManager defaultErrorManager() { * @param name the error manager class name. * @throws SecurityException if not allowed. */ + @SuppressWarnings("this-escape") private void initErrorManager(final String name) { - assert Thread.holdsLock(this); try { if (name != null) { setErrorManager0(LogManagerProperties.newErrorManager(name)); @@ -2980,7 +3069,8 @@ private void initErrorManager(final String name) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError("Unable to create error manager", + E, ErrorManager.OPEN_FAILURE); } } @@ -3008,7 +3098,8 @@ private void initFormatter(final String name) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError("Unable to create formatter", + E, ErrorManager.OPEN_FAILURE); formatter = createSimpleFormatter(); } } @@ -3030,7 +3121,7 @@ private void initComparator(final String name) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError("Unable to create comparator", E, ErrorManager.OPEN_FAILURE); } } @@ -3042,8 +3133,31 @@ private void initComparatorReverse(final String reverse) { } else { IllegalArgumentException E = new IllegalArgumentException( "No comparator to reverse."); - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError(reverse, E, ErrorManager.OPEN_FAILURE); + } + } + } + + /** + * Gets and assigns the content types for this handler. + * + * @since Angus Mail 2.0.3 + */ + private void initContentTypes() { + try { + Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); + try { + this.contentTypes = FileTypeMap.getDefaultFileTypeMap(); + } finally { //reset ccl before reporting errors + getAndSetContextClassLoader(ccl); } + } catch (final RuntimeException re) { + reportError("Unable to get default FileTypeMap", + re, ErrorManager.OPEN_FAILURE); + } catch (final LinkageError GLASSFISH_21258) { + reportLinkageError(GLASSFISH_21258, ErrorManager.OPEN_FAILURE); + } catch (final ServiceConfigurationError sce) { + reportConfigurationError(sce, ErrorManager.OPEN_FAILURE); } } @@ -3062,7 +3176,7 @@ private void initPushLevel(final String nameOrNumber) { this.pushLevel = Level.OFF; } } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.OPEN_FAILURE); + reportError("Unable to parse push level", RE, ErrorManager.OPEN_FAILURE); } if (this.pushLevel == null) { @@ -3087,7 +3201,7 @@ private void initPushFilter(final String name) { } catch (final SecurityException SE) { throw SE; //Avoid catch all. } catch (final Exception E) { - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError("Unable to create push filter", E, ErrorManager.OPEN_FAILURE); } } @@ -3117,7 +3231,7 @@ private void initSubject(String name) { this.subjectFormatter = TailNameFormatter.of(name); } catch (final Exception E) { this.subjectFormatter = TailNameFormatter.of(name); - reportError(E.getMessage(), E, ErrorManager.OPEN_FAILURE); + reportError("Unable to create subject formatter", E, ErrorManager.OPEN_FAILURE); } } else { //User has forced empty or literal null. this.subjectFormatter = TailNameFormatter.of(name); @@ -3188,6 +3302,8 @@ private void push(final boolean priority, final int code) { } } catch (final LinkageError JDK8152515) { reportLinkageError(JDK8152515, code); + } catch (final ServiceConfigurationError sce) { + reportConfigurationError(sce, code); } finally { releaseMutex(); } @@ -3238,7 +3354,7 @@ private void sort() { } } } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + reportError(comparator.toString(), RE, ErrorManager.FORMAT_FAILURE); } } } @@ -3252,6 +3368,8 @@ private void sort() { * @return null if there are no records or is currently in a push. * Otherwise a new message is created with a formatted message and * attached session. + * @throws ServiceConfigurationError if the session provider fails to + * lookup the provider implementation. */ private Message writeLogRecords(final int code) { try { @@ -3269,7 +3387,7 @@ private Message writeLogRecords(final int code) { } } } catch (final Exception e) { - reportError(e.getMessage(), e, code); + reportError("Unable to create message", e, code); } return null; } @@ -3284,6 +3402,8 @@ private Message writeLogRecords(final int code) { * @throws MessagingException if there is a problem. * @throws IOException if there is a problem. * @throws RuntimeException if there is an unexpected problem. + * @throws ServiceConfigurationError if the session provider fails to + * lookup the provider implementation. * @since JavaMail 1.5.3 */ private Message writeLogRecords0() throws Exception { @@ -3465,6 +3585,8 @@ private boolean verifySettings(final Session session) { } } catch (final LinkageError JDK8152515) { reportLinkageError(JDK8152515, ErrorManager.OPEN_FAILURE); + } catch (final ServiceConfigurationError sce) { + reportConfigurationError(sce, ErrorManager.OPEN_FAILURE); } return false; } @@ -3907,10 +4029,17 @@ private void setErrorContent(MimeMessage msg, String verify, Throwable t) { */ private Session updateSession() { assert Thread.holdsLock(this); - final Session settings; + Session settings = null; if (mailProps.getProperty("verify") != null) { - settings = initSession(); - assert settings == session : session; + try { + settings = initSession(); + assert settings == session : session; + } catch (final RuntimeException re) { + reportError("Unable to update session", + re, ErrorManager.OPEN_FAILURE); + } catch (final ServiceConfigurationError sce) { + reportConfigurationError(sce, ErrorManager.OPEN_FAILURE); + } } else { session = null; //Remove old session. settings = null; @@ -3922,6 +4051,10 @@ private Session updateSession() { * Creates a session using a proxy properties object. * * @return the session that was created and assigned. + * @throws IllegalStateException if the session provider fails to lookup the + * provider implementation. + * @throws ServiceConfigurationError if the session provider fails to + * lookup the provider implementation. */ private Session initSession() { assert Thread.holdsLock(this); @@ -3963,10 +4096,17 @@ private void envelopeFor(Message msg, boolean priority) { try { msg.setSentDate(new java.util.Date()); } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Sent date not set", ME, ErrorManager.FORMAT_FAILURE); } } + /** + * Creates a new MimeMultipart. + * + * @return a new MimeMultipart + * @throws MessagingException if there is a problem. + * @since Angus Mail 2.0.3 + */ private MimeMultipart createMultipart() throws MessagingException { assert Thread.holdsLock(this); final Object ccl = getAndSetContextClassLoader(MAILHANDLER_LOADER); @@ -4122,7 +4262,7 @@ private void appendFileName0(final Part part, String chunk) { final String old = part.getFileName(); part.setFileName(old != null ? old.concat(chunk) : chunk); } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("File name truncated", ME, ErrorManager.FORMAT_FAILURE); } } @@ -4159,7 +4299,7 @@ private void appendSubject0(final Message msg, String chunk) { ((MimeMessage) msg).setSubject(old != null ? old.concat(chunk) : chunk, MimeUtility.mimeCharset(charset)); } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Subject truncated", ME, ErrorManager.FORMAT_FAILURE); } } @@ -4239,7 +4379,8 @@ private void appendContentLang(final MimePart p, final Locale l) { } } } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Content-Language not set", + ME, ErrorManager.FORMAT_FAILURE); } } @@ -4258,7 +4399,8 @@ private void setAcceptLang(final Part p) { p.setHeader("Accept-Language", lang); } } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Accept-Language not set", + ME, ErrorManager.FORMAT_FAILURE); } } @@ -4334,7 +4476,7 @@ private String head(final Formatter f) { try { return f.getHead(this); } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + reportError("head", RE, ErrorManager.FORMAT_FAILURE); return ""; } } @@ -4350,7 +4492,7 @@ private String format(final Formatter f, final LogRecord r) { try { return f.format(r); } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + reportError("format", RE, ErrorManager.FORMAT_FAILURE); return ""; } } @@ -4366,7 +4508,7 @@ private String tail(final Formatter f, final String def) { try { return f.getTail(this); } catch (final RuntimeException RE) { - reportError(RE.getMessage(), RE, ErrorManager.FORMAT_FAILURE); + reportError("tail", RE, ErrorManager.FORMAT_FAILURE); return def; } } @@ -4387,7 +4529,7 @@ private void setMailer(final Message msg) { try { value = MimeUtility.encodeText(k.getName()); } catch (final UnsupportedEncodingException E) { - reportError(E.getMessage(), E, ErrorManager.FORMAT_FAILURE); + reportError(k.getName(), E, ErrorManager.FORMAT_FAILURE); value = k.getName().replaceAll("[^\\x00-\\x7F]", "\uu001A"); } value = MimeUtility.fold(10, mail.getName() + " using the " @@ -4395,7 +4537,8 @@ private void setMailer(final Message msg) { } msg.setHeader("X-Mailer", value); } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("X-Mailer not set", + ME, ErrorManager.FORMAT_FAILURE); } } @@ -4410,7 +4553,8 @@ private void setPriority(final Message msg) { msg.setHeader("Priority", "urgent"); msg.setHeader("X-Priority", "2"); //High } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Importance and priority not set", + ME, ErrorManager.FORMAT_FAILURE); } } @@ -4429,7 +4573,8 @@ private void setIncompleteCopy(final Message msg) { try { msg.setHeader("Incomplete-Copy", ""); } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Incomplete-Copy not set", + ME, ErrorManager.FORMAT_FAILURE); } } @@ -4445,7 +4590,8 @@ private void setAutoSubmitted(final Message msg) { try { //RFC 3834 (5.2) msg.setHeader("auto-submitted", "auto-generated"); } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("auto-submitted not set", + ME, ErrorManager.FORMAT_FAILURE); } } } @@ -4472,7 +4618,8 @@ private void setFrom(final Message msg) { //to fail. Assume the user wants to omit the from address //header. } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("From address not set", + ME, ErrorManager.FORMAT_FAILURE); setDefaultFrom(msg); } } else { @@ -4489,7 +4636,8 @@ private void setDefaultFrom(final Message msg) { try { msg.setFrom(); } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Default from address not set", + ME, ErrorManager.FORMAT_FAILURE); } } @@ -4518,7 +4666,7 @@ private void setDefaultRecipient(final Message msg, } } } catch (MessagingException | RuntimeException ME) { - reportError("Unable to compute a default recipient.", + reportError("Default recipient not set", ME, ErrorManager.FORMAT_FAILURE); } } @@ -4537,7 +4685,8 @@ private void setReplyTo(final Message msg) { msg.setReplyTo(address); } } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Reply-To address not set", + ME, ErrorManager.FORMAT_FAILURE); } } } @@ -4563,7 +4712,7 @@ private void setSender(final Message msg) { } } } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError("Sender not set", ME, ErrorManager.FORMAT_FAILURE); } } } @@ -4600,7 +4749,7 @@ private boolean setRecipient(final Message msg, msg.setRecipients(type, address); } } catch (final MessagingException ME) { - reportError(ME.getMessage(), ME, ErrorManager.FORMAT_FAILURE); + reportError(key, ME, ErrorManager.FORMAT_FAILURE); } } return containsKey; @@ -4734,7 +4883,7 @@ private String getLocalHost(final Service s) { } catch (SecurityException | NoSuchMethodException | LinkageError ignore) { } catch (final Exception ex) { - reportError(s.toString(), ex, ErrorManager.OPEN_FAILURE); + reportError(String.valueOf(s), ex, ErrorManager.OPEN_FAILURE); } return null; } diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java index cbd2005..c5bfd04 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java @@ -506,14 +506,13 @@ private void testLoggable(Level lvl, LogRecord record) { instance.setErrorManager(em); instance.setLevel(lvl); - boolean result = false; boolean expect = true; if (record == null || record.getLevel().intValue() < lvl.intValue() || Level.OFF.intValue() == lvl.intValue()) { expect = false; } - result = instance.isLoggable(record); + boolean result = instance.isLoggable(record); assertEquals(lvl.getName(), expect, result); instance.setLevel(Level.INFO); @@ -1141,14 +1140,14 @@ public void testSingleSortComparator() { } InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Throwable t : em.exceptions) { - if (isConnectOrTimeout(t)) { - continue; - } else { + if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -1165,19 +1164,21 @@ public void testSingleSortViolateContract() { boolean seenError = false; InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Throwable t : em.exceptions) { if (isConnectOrTimeout(t)) { continue; - } else if (t.getClass() == IllegalArgumentException.class + } + if (t.getClass() == IllegalArgumentException.class && t.getMessage().contains(instance.getComparator() .getClass().getName())) { seenError = true; //See Arrays.sort(T[], Comparator) continue; //expect. - } else { - dump(t); - fail(t.toString()); } + dump(t); + failed = true; } + assertFalse(failed); assertTrue("Exception was not thrown.", seenError); assertFalse(em.exceptions.isEmpty()); } @@ -1209,18 +1210,19 @@ private void testThrowComparator(int records) { InternalErrorManager em = internalErrorManagerFrom(instance); boolean seenError = false; + boolean failed = false; for (Throwable t : em.exceptions) { if (isConnectOrTimeout(t)) { continue; - } else if (t.getClass() == RuntimeException.class) { + } + if (t.getClass() == RuntimeException.class) { seenError = true; continue; //expect. - } else { - dump(t); - fail(t.toString()); } + dump(t); + failed = true; } - + assertFalse(failed); if (records == 0) { assertEquals(true, em.exceptions.isEmpty()); } else { @@ -1593,12 +1595,14 @@ public void testStatefulFilter() { } h.close(); assertEquals(MAX_RECORDS, cf.count); + boolean failed = false; for (Exception exception : em.exceptions) { if (!isConnectOrTimeout(exception)) { dump(exception); - fail(String.valueOf(exception)); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -1625,12 +1629,14 @@ public void testStatefulAttachmentFilter() { assertEquals(MAX_RECORDS, negativeOne.count); assertEquals(MAX_RECORDS, one.count); assertEquals(MAX_RECORDS, two.count); + boolean failed = false; for (Exception exception : em.exceptions) { if (!isConnectOrTimeout(exception)) { dump(exception); - fail(String.valueOf(exception)); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -1666,12 +1672,14 @@ private void testStatefulAttachmentFilter(boolean clear) { assertEquals(MAX_RECORDS, cf.count); assertEquals(MAX_RECORDS, one.count); + boolean failed = false; for (Exception exception : em.exceptions) { if (!isConnectOrTimeout(exception)) { dump(exception); - fail(String.valueOf(exception)); + failed = false; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -1694,12 +1702,14 @@ public void testStatefulPushFilter() { h.publish(r); h.close(); assertEquals(1, cf.count); + boolean failed = false; for (Exception exception : em.exceptions) { if (!isConnectOrTimeout(exception)) { dump(exception); - fail(String.valueOf(exception)); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -1748,12 +1758,14 @@ public boolean isLoggable(LogRecord record) { assertEquals(1, two.count); assertEquals(1, push.count); } + boolean failed = false; for (Exception exception : em.exceptions) { if (!isConnectOrTimeout(exception)) { dump(exception); - fail(String.valueOf(exception)); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -2096,14 +2108,16 @@ public String getTail(Handler h) { } instance.flush(); + boolean failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if ((t instanceof MessagingException == false) && (t instanceof IllegalStateException == false)) { dump(t); - fail(String.valueOf(t)); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -2366,13 +2380,15 @@ private void testCloseContextClassLoader0() { instance.close(); } + boolean failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); } @Test @@ -2523,18 +2539,20 @@ public void testLevelBeforeClose() { instance.setFormatter(new LevelCheckingFormatter(expect)); instance.close(); + boolean failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException) { if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } else { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -2550,18 +2568,20 @@ public void testLevelAfterClose() { assertEquals(Level.OFF, instance.getLevel()); instance.close(); + boolean failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException) { if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } else { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -2575,6 +2595,7 @@ public void testLogManagerReset() throws IOException { manager.reset(); + boolean failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException) { @@ -2583,17 +2604,19 @@ public void testLogManagerReset() throws IOException { } if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } else { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); instance = startLogManagerReset("local"); em = internalErrorManagerFrom(instance); + failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException) { @@ -2602,16 +2625,18 @@ public void testLogManagerReset() throws IOException { } if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } else { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); manager.reset(); + failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException) { @@ -2620,13 +2645,14 @@ public void testLogManagerReset() throws IOException { } if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } else { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); String[] noVerify = new String[]{null, "", "null"}; for (int v = 0; v < noVerify.length; v++) { @@ -2666,16 +2692,18 @@ public void testLogManagerReset() throws IOException { //Allow the LogManagerProperties to copy on a bad enum type. boolean foundIllegalArg = false; + failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof IllegalArgumentException) { foundIllegalArg = true; } else if (t instanceof RuntimeException) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertTrue(foundIllegalArg); assertFalse(em.exceptions.isEmpty()); } finally { @@ -3229,14 +3257,15 @@ protected void error(MimeMessage msg, Throwable t, int code) { target.close(); + boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); for (Exception t : em.exceptions) { - if (isConnectOrTimeout(t)) { - continue; + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; } - dump(t); - fail(t.toString()); } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { Locale.setDefault(l); @@ -3347,14 +3376,15 @@ private void testContentLangInfer(MailHandler target, String logPrefix, String b target.close(); + boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); for (Exception t : em.exceptions) { - if (isConnectOrTimeout(t)) { - continue; + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; } - dump(t); - fail(t.toString()); } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -3470,14 +3500,15 @@ protected void error(MimeMessage msg, Throwable t, int code) { target.close(); + boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); for (Exception t : em.exceptions) { - if (isConnectOrTimeout(t)) { - continue; + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; } - dump(t); - fail(t.toString()); } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -3883,15 +3914,15 @@ public void testMailProperties() throws Exception { props.setProperty("mail.to", "localhost@localdomain"); instance.setMailProperties(props); instance.flush(); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; - if (isConnectOrTimeout(t)) { - continue; - } else { + if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); props.setProperty("mail.from", "localhost@localdomain"); @@ -3903,17 +3934,16 @@ public void testMailProperties() throws Exception { instance.publish(new LogRecord(Level.SEVERE, "test")); instance.close(); - int failed = 0; + failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException || isConnectOrTimeout(t)) { continue; - } else { - dump(t); - failed++; } + dump(t); + failed = true; } - assertEquals(0, failed); + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -3936,6 +3966,7 @@ public void testInitMailEntriesNullMailProperties() throws Exception { assertEquals("localhost@localdomain", stored.getProperty("mail.from")); assertEquals("local",stored.getProperty("verify")); + boolean failed = false; for (Exception e : em.exceptions) { if (e instanceof AddressException) { if (e.toString().contains("badAddress")) { @@ -3943,8 +3974,9 @@ public void testInitMailEntriesNullMailProperties() throws Exception { } } dump(e); - fail(e.toString()); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); target.setMailProperties((Properties) null); @@ -3952,6 +3984,7 @@ public void testInitMailEntriesNullMailProperties() throws Exception { assertEquals("localhost@localdomain", stored.getProperty("mail.from")); assertEquals("local", stored.getProperty("verify")); + failed = false; for (Exception e : em.exceptions) { if (e instanceof AddressException) { if (e.toString().contains("badAddress")) { @@ -3959,12 +3992,14 @@ public void testInitMailEntriesNullMailProperties() throws Exception { } } dump(e); - fail(e.toString()); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); target = new MailHandler((Properties) null); em = internalErrorManagerFrom(target); + failed = false; for (Exception e : em.exceptions) { if (e instanceof AddressException) { if (e.toString().contains("badAddress")) { @@ -3972,8 +4007,9 @@ public void testInitMailEntriesNullMailProperties() throws Exception { } } dump(e); - fail(e.toString()); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); stored = target.getMailProperties(); @@ -4013,6 +4049,7 @@ public void testInitMailEntriesNullSetMailEntries() throws Exception { InternalErrorManager em = internalErrorManagerFrom(target); target.setMailEntries((String) null); Properties stored = target.getMailProperties(); + boolean failed = false; for (Exception e : em.exceptions) { if (e instanceof AddressException) { if (e.toString().contains("badAddress")) { @@ -4020,8 +4057,9 @@ public void testInitMailEntriesNullSetMailEntries() throws Exception { } } dump(e); - fail(e.toString()); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); assertEquals("localhost@localdomain", stored.getProperty("mail.from")); @@ -4346,16 +4384,16 @@ private List
asList(Address... a) { instance.setMailProperties(addresses); instance.close(); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (isConnectOrTimeout(t) || t instanceof SendFailedException) { continue; - } else { - dump(t); - fail(t.toString()); } + dump(t); + failed = true; } - + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -4374,6 +4412,7 @@ public void testInvalidDefaultRecipient() throws Exception { instance.setMailProperties(props); instance.close(); InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Exception exception : em.exceptions) { if (exception instanceof MessagingException) { if (exception instanceof AddressException @@ -4390,8 +4429,9 @@ public void testInvalidDefaultRecipient() throws Exception { } } dump(exception); - fail(exception.toString()); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -4791,19 +4831,21 @@ public void testAttachmentFilterSwapBeforePush() { instance.close(); int seenFormat = 0; + boolean failed = false; for (Exception exception : em.exceptions) { if (exception instanceof MessagingException) { continue; - } else if (exception instanceof RuntimeException + } + if (exception instanceof RuntimeException && exception.getMessage().contains(instance.getFilter().toString()) && exception.getMessage().contains(Arrays.asList(instance.getAttachmentFilters()).toString())) { seenFormat++; continue; //expected. - } else { - fail(String.valueOf(exception)); } + failed = true; } assertTrue("No format error", seenFormat > 0); + assertFalse(failed); } @Test @@ -4959,14 +5001,16 @@ private void testPushFilterReentrance(int records, int cap) { } instance.close(); + boolean failed = false; for (Exception exception : em.exceptions) { Throwable t = exception; if ((t instanceof MessagingException == false) && (t instanceof IllegalStateException == false)) { dump(t); - fail(String.valueOf(t)); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { logger.removeHandler(instance); @@ -5327,14 +5371,16 @@ public void testVerifyErrorManager() throws Exception { //ensure VerifyErrorManager was installed. assertEquals(VerifyErrorManager.class, em.getClass()); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (!isConnectOrTimeout(t)) { dump(t); - fail(t.toString()); + failed = false; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); instance.close(); } finally { @@ -5466,6 +5512,7 @@ public void testIntern() throws Exception { assertSame(names[5], names[6]); InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof IllegalArgumentException @@ -5473,7 +5520,9 @@ public void testIntern() throws Exception { continue; } dump(t); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } @@ -5505,6 +5554,7 @@ public void testInternNonDiscriminating() throws Exception { instance.close(); InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof IllegalArgumentException @@ -5512,7 +5562,9 @@ public void testInternNonDiscriminating() throws Exception { continue; } dump(t); + failed = true; } + assertFalse(failed); assertEquals(2, em.exceptions.size()); } @@ -5535,7 +5587,6 @@ public void testComparatorReverse() throws Exception { for (Exception exception : em.exceptions) { final Throwable t = exception; dump(t); - fail(t.toString()); } assertTrue(em.exceptions.isEmpty()); @@ -5553,7 +5604,6 @@ public void testComparatorReverse() throws Exception { for (Exception exception : em.exceptions) { final Throwable t = exception; dump(t); - fail(t.toString()); } assertTrue(em.exceptions.isEmpty()); @@ -5571,7 +5621,6 @@ public void testComparatorReverse() throws Exception { for (Exception exception : em.exceptions) { final Throwable t = exception; dump(t); - fail(t.toString()); } assertTrue(em.exceptions.isEmpty()); @@ -5585,15 +5634,16 @@ public void testComparatorReverse() throws Exception { instance = new MailHandler(); instance.close(); em = internalErrorManagerFrom(instance); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof IllegalArgumentException) { continue; } dump(t); - fail(t.toString()); + failed = true; } - + assertFalse(failed); assertFalse(IllegalArgumentException.class.getName(), em.exceptions.isEmpty()); @@ -5602,15 +5652,16 @@ public void testComparatorReverse() throws Exception { instance = new MailHandler(); instance.close(); em = internalErrorManagerFrom(instance); + failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof IllegalArgumentException) { continue; } dump(t); - fail(t.toString()); + failed = true; } - + assertFalse(failed); assertFalse(IllegalArgumentException.class.getName(), em.exceptions.isEmpty()); @@ -5619,15 +5670,16 @@ public void testComparatorReverse() throws Exception { instance = new MailHandler(); instance.close(); em = internalErrorManagerFrom(instance); + failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof IllegalArgumentException) { continue; } dump(t); - fail(t.toString()); + failed = true; } - + assertFalse(failed); assertFalse(IllegalArgumentException.class.getName(), em.exceptions.isEmpty()); } finally { @@ -5657,13 +5709,15 @@ public void testVerifyLogManager() throws Exception { assertEquals(InternalErrorManager.class, em.getClass()); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); instance.close(); @@ -5677,13 +5731,15 @@ public void testVerifyLogManager() throws Exception { assertEquals(InternalErrorManager.class, em.getClass()); + failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); instance.close(); @@ -5697,6 +5753,7 @@ public void testVerifyLogManager() throws Exception { assertEquals(InternalErrorManager.class, em.getClass()); + failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (isConnectOrTimeout(t)) { @@ -5704,9 +5761,10 @@ public void testVerifyLogManager() throws Exception { } if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); instance.close(); @@ -5719,17 +5777,19 @@ public void testVerifyLogManager() throws Exception { assertEquals(InternalErrorManager.class, em.getClass()); + failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException) { continue; - } else if (isConnectOrTimeout(t)) { + } + if (isConnectOrTimeout(t)) { continue; - } else { - dump(t); - fail(t.toString()); } + dump(t); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { manager.reset(); @@ -5748,13 +5808,15 @@ public void testVerifyProperties() throws Exception { instance.setErrorManager(em); instance.setMailProperties(props); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); } finally { instance.close(); } @@ -5766,13 +5828,15 @@ public void testVerifyProperties() throws Exception { instance.setErrorManager(em); instance.setMailProperties(props); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); } finally { instance.close(); } @@ -5784,6 +5848,7 @@ public void testVerifyProperties() throws Exception { instance.setErrorManager(em); instance.setMailProperties(props); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (isConnectOrTimeout(t)) { @@ -5791,9 +5856,10 @@ public void testVerifyProperties() throws Exception { } if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); } finally { instance.close(); } @@ -5805,6 +5871,7 @@ public void testVerifyProperties() throws Exception { instance.setErrorManager(em); instance.setMailProperties(props); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (isConnectOrTimeout(t)) { @@ -5812,9 +5879,10 @@ public void testVerifyProperties() throws Exception { } if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); } finally { instance.close(); } @@ -5826,17 +5894,19 @@ public void testVerifyProperties() throws Exception { instance.setErrorManager(em); instance.setMailProperties(props); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException) { continue; - } else if (isConnectOrTimeout(t)) { + } + if (isConnectOrTimeout(t)) { continue; - } else { - dump(t); - fail(t.toString()); } + dump(t); + failed = true; } + assertFalse(failed); } finally { instance.close(); } @@ -5863,13 +5933,15 @@ public void testVerifyPropertiesConstructor() throws Exception { try { InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { instance.close(); @@ -5887,13 +5959,15 @@ public void testVerifyPropertiesConstructor() throws Exception { try { InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { instance.close(); @@ -5904,7 +5978,7 @@ public void testVerifyPropertiesConstructor() throws Exception { instance = new MailHandler(props); try { InternalErrorManager em = internalErrorManagerFrom(instance); - + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (isConnectOrTimeout(t)) { @@ -5912,9 +5986,10 @@ public void testVerifyPropertiesConstructor() throws Exception { } if (t instanceof AddressException == false) { dump(t); - fail(t.toString()); + failed = true; } } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { instance.close(); @@ -5925,17 +6000,19 @@ public void testVerifyPropertiesConstructor() throws Exception { try { InternalErrorManager em = internalErrorManagerFrom(instance); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof AddressException) { continue; - } else if (isConnectOrTimeout(t)) { + } + if (isConnectOrTimeout(t)) { continue; - } else { - dump(t); - fail(t.toString()); } + dump(t); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { instance.close(); @@ -6167,13 +6244,16 @@ public void testInitAttachmentFilters() throws Exception { target = new MailHandler(); try { em = internalErrorManagerFrom(target); + boolean failed = false; for (Exception exception : em.exceptions) { final Throwable t = exception; if (t instanceof IndexOutOfBoundsException) { continue; } dump(t); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { target.close(); @@ -6542,6 +6622,7 @@ private void testInitException(Properties props) throws Exception { final MailHandler target = new MailHandler(); try { InternalErrorManager em = internalErrorManagerFrom(target); + boolean failed = false; next: for (Exception t : em.exceptions) { for (Throwable cause = t; cause != null; cause = cause.getCause()) { @@ -6550,8 +6631,9 @@ private void testInitException(Properties props) throws Exception { } } dump(t); - fail(t.toString()); + failed = true; } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); } finally { target.close(); @@ -6897,17 +6979,17 @@ private void initGoodTest(Class type, assertEquals(EmptyFormatter.class, h.getSubject().getClass()); assertEquals(EmptyAuthenticator.class, h.getAuthenticator().getClass()); assertEquals(3, h.getAttachmentFormatters().length); - assertTrue(null != h.getAttachmentFormatters()[0]); - assertTrue(null != h.getAttachmentFormatters()[1]); - assertTrue(null != h.getAttachmentFormatters()[2]); + assertNotNull(h.getAttachmentFormatters()[0]); + assertNotNull(h.getAttachmentFormatters()[1]); + assertNotNull(h.getAttachmentFormatters()[2]); assertEquals(3, h.getAttachmentFilters().length); - assertEquals(null, h.getAttachmentFilters()[0]); + assertNull(h.getAttachmentFilters()[0]); assertEquals(ThrowFilter.class, h.getAttachmentFilters()[1].getClass()); assertEquals(ThrowFilter.class, h.getAttachmentFilters()[2].getClass()); assertEquals(3, h.getAttachmentNames().length); - assertTrue(null != h.getAttachmentNames()[0]); - assertTrue(null != h.getAttachmentNames()[1]); - assertTrue(null != h.getAttachmentNames()[2]); + assertNotNull(h.getAttachmentNames()[0]); + assertNotNull(h.getAttachmentNames()[1]); + assertNotNull(h.getAttachmentNames()[2]); InternalErrorManager em = internalErrorManagerFrom(h); for (Exception exception : em.exceptions) { @@ -7016,12 +7098,10 @@ private void initBadTest(Class type, h = type.getConstructor(types).newInstance(params); System.err.flush(); result = oldErrors.toString(encoding).trim(); - int index = result.indexOf(ErrorManager.class.getName() + ": " - + ErrorManager.OPEN_FAILURE + ": " + Level.SEVERE.getName() - + ": InvalidErrorManager"); - assertTrue(index > -1); - assertTrue(result.indexOf( - "java.lang.ClassNotFoundException: InvalidErrorManager") > index); + assertTrue(result.startsWith(ErrorManager.class.getName() + ": " + + ErrorManager.OPEN_FAILURE + ": " + Level.SEVERE.getName())); + assertTrue(result, result.contains( + "java.lang.ClassNotFoundException: InvalidErrorManager")); oldErrors.reset(); } finally { System.setErr(err); @@ -7029,38 +7109,40 @@ private void initBadTest(Class type, assert h != null; assertEquals(ErrorManager.class, h.getErrorManager().getClass()); - assertTrue(h.getCapacity() != 10); - assertTrue(h.getCapacity() != -10); + assertNotEquals(10, h.getCapacity()); + assertNotEquals(-10, h.getCapacity()); assertEquals(Level.WARNING, h.getLevel()); - assertEquals(null, h.getFilter()); + assertNull( h.getFilter()); assertEquals(SimpleFormatter.class, h.getFormatter().getClass()); assertEquals(Level.OFF, h.getPushLevel()); - assertEquals(null, h.getPushFilter()); + assertNull(h.getPushFilter()); assertNull(h.getComparator()); - assertEquals(null, h.getEncoding()); + assertNull(h.getEncoding()); assertEquals(ThrowFilter.class.getName(), h.getSubject().toString()); PasswordAuthentication pa = passwordAuthentication(h.getAuthenticator(), "user"); assertEquals("user", pa.getUserName()); assertEquals("password", pa.getPassword()); assertEquals(3, h.getAttachmentFormatters().length); - assertTrue(null != h.getAttachmentFormatters()[0]); - assertTrue(null != h.getAttachmentFormatters()[1]); - assertTrue(null != h.getAttachmentFormatters()[2]); + assertNotNull(h.getAttachmentFormatters()[0]); + assertNotNull(h.getAttachmentFormatters()[1]); + assertNotNull(h.getAttachmentFormatters()[2]); assertEquals(3, h.getAttachmentFilters().length); - assertTrue(null == h.getAttachmentFilters()[0]); - assertTrue(null == h.getAttachmentFilters()[1]); - assertTrue(null != h.getAttachmentFilters()[2]); + assertNull(h.getAttachmentFilters()[0]); + assertNull(h.getAttachmentFilters()[1]); + assertNotNull(h.getAttachmentFilters()[2]); assertEquals(ThrowFilter.class, h.getAttachmentFilters()[2].getClass()); assertEquals(3, h.getAttachmentNames().length); - assertTrue(null != h.getAttachmentNames()[0]); - assertTrue(null != h.getAttachmentNames()[1]); - assertTrue(null != h.getAttachmentNames()[2]); + assertNotNull(h.getAttachmentNames()[0]); + assertNotNull(h.getAttachmentNames()[1]); + assertNotNull(h.getAttachmentNames()[2]); assertEquals(XMLFormatter.class, h.getAttachmentNames()[2].getClass()); h.close(); } private static boolean isConnectOrTimeout(Throwable t) { - if (t instanceof MessagingException) { + if (t == null) { + return false; + } else if (t instanceof MessagingException) { Throwable cause = t.getCause(); if (cause == null) { //GNU JavaMail doesn't support 1.4 chaining. cause = ((MessagingException) t).getNextException(); From c3f4b55d757ba262e583c2630cadcc01a6458b67 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Mon, 29 Jan 2024 22:36:07 -0600 Subject: [PATCH 02/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../angus/mail/util/logging/MailHandler.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java index f50ffa1..ae76a01 100644 --- a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java +++ b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java @@ -1954,7 +1954,8 @@ public synchronized final void setSubjectFormatter(final Formatter format) { */ @Override protected void reportError(String msg, Exception ex, int code) { - //For a this-escape, the error manager will be the default. + //This method is not protected from a this-escape. + //The error manager will be non-null in any case. try { if (msg != null) { errorManager.error(Level.SEVERE.getName() @@ -2186,6 +2187,11 @@ private void reportLinkageError(final Throwable le, final int code) { if (idx == null || idx > MUTEX_LINKAGE) { MUTEX.set(MUTEX_LINKAGE); try { + //Per the API docs this is not how uncaught exception handler + //should be used. However, the throwable that we are receiving + //here is happening only when the JVM is shutting down. + //This will only execute on unpatched systems. + //See tickets listed in API docs above. Thread.currentThread().getUncaughtExceptionHandler() .uncaughtException(Thread.currentThread(), le); } catch (RuntimeException | ServiceConfigurationError @@ -4204,10 +4210,13 @@ private String descriptionFrom(Formatter f, Filter filter, Formatter name) { * * @param f the formatter. * @return a class name that represents the given formatter. - * @throws NullPointerException if the parameter is null. * @since JavaMail 1.4.5 */ private String getClassId(final Formatter f) { + if (f == null) { + return "null"; + } + if (f instanceof TailNameFormatter) { return String.class.getName(); //Literal string. } else { From af5caf3df01e0bf34b8c7f796c7b94c3041f0f46 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Mon, 29 Jan 2024 22:42:12 -0600 Subject: [PATCH 03/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../java/org/eclipse/angus/mail/util/logging/MailHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java index ae76a01..1c87527 100644 --- a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java +++ b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java @@ -939,7 +939,6 @@ public void close() { } finally { //Change level after formatting. this.logLevel = Level.OFF; this.disabledLevel = null; //free reference - /** * The sign bit of the capacity is set to ensure that * records that have passed isLoggable, but have yet to be From 47d8a5e8dbd16ab9127f05df15923f4246a4dd5e Mon Sep 17 00:00:00 2001 From: jmehrens Date: Mon, 29 Jan 2024 22:53:10 -0600 Subject: [PATCH 04/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../java/org/eclipse/angus/mail/util/logging/MailHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java index 1c87527..c1ab997 100644 --- a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java +++ b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java @@ -4042,6 +4042,8 @@ private Session updateSession() { } catch (final RuntimeException re) { reportError("Unable to update session", re, ErrorManager.OPEN_FAILURE); + } catch (final LinkageError GLASSFISH_21258) { + reportLinkageError(GLASSFISH_21258, ErrorManager.OPEN_FAILURE); } catch (final ServiceConfigurationError sce) { reportConfigurationError(sce, ErrorManager.OPEN_FAILURE); } From ed8f3dbf5b6c62d26269b4dd3ced286b97eef5fc Mon Sep 17 00:00:00 2001 From: jmehrens Date: Mon, 29 Jan 2024 23:43:14 -0600 Subject: [PATCH 05/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../angus/mail/util/logging/MailHandler.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java index c1ab997..95fe9a3 100644 --- a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java +++ b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java @@ -2146,6 +2146,13 @@ private void reportError(Message msg, Exception ex, int code) { } } + /** + * Report a ServiceConfigurationError to the error manager. + * + * @param t the service configuration error. + * @param code the error manager reason code. + * @since Angus Mail 2.0.3 + */ private void reportConfigurationError(Throwable t, int code) { final Integer idx = MUTEX.get(); if (idx == null || idx > MUTEX_SERVICE) { @@ -2288,9 +2295,8 @@ private String contentWithEncoding(String type, String encoding) { /** * Sets the capacity for this handler. * - * @param newCapacity the max number of records. - * @throws SecurityException if a security manager exists and the - * caller does not have LoggingPermission("control"). + * @param newCapacity the max number of records. Default capacity is used if + * the value is negative. * @throws IllegalStateException if called from inside a push. */ private synchronized void setCapacity0(int newCapacity) { @@ -4209,7 +4215,7 @@ private String descriptionFrom(Formatter f, Filter filter, Formatter name) { * Gets a class name represents the behavior of the formatter. * The class name may not be assignable to a Formatter. * - * @param f the formatter. + * @param f the formatter or null. * @return a class name that represents the given formatter. * @since JavaMail 1.4.5 */ From 66442b05dd92342af0468ed865070090f370e574 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Tue, 30 Jan 2024 22:42:58 -0600 Subject: [PATCH 06/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../angus/mail/util/logging/MailHandler.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java index 95fe9a3..46967cf 100644 --- a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java +++ b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java @@ -722,6 +722,9 @@ private void publish0(final LogRecord record) { Message msg; boolean priority; synchronized (this) { + //No need to check for sealed as long as the init method ensures + //that data.length and capacity are both zero until end of init(). + //size is always zero on construction. if (size == data.length && size < capacity) { grow(); } @@ -1983,7 +1986,7 @@ protected void reportError(String msg, Exception ex, int code) { * does not have {@code LoggingPermission("control")}. */ private void checkAccess() { - if (sealed) { + if (this.sealed) { LogManagerProperties.checkLogManagerAccess(); } else { throw new SecurityException("this-escape"); @@ -2304,7 +2307,7 @@ private synchronized void setCapacity0(int newCapacity) { throw new IllegalStateException(); } - if (this.capacity == 0) { + if (!this.sealed || this.capacity == 0) { return; } @@ -2315,7 +2318,7 @@ private synchronized void setCapacity0(int newCapacity) { if (this.capacity < 0) { //If closed, remain closed. this.capacity = -newCapacity; } else { - this.push(false, ErrorManager.FLUSH_FAILURE); + push(false, ErrorManager.FLUSH_FAILURE); this.capacity = newCapacity; if (this.data.length > newCapacity) { initLogRecords(1); @@ -2448,9 +2451,9 @@ private boolean alignAttachmentFilters(int expect) { private void reset() { assert Thread.holdsLock(this); if (size < data.length) { - Arrays.fill(data, 0, size, null); + Arrays.fill(data, 0, size, (LogRecord) null); } else { - Arrays.fill(data, null); + Arrays.fill(data, (LogRecord) null); } this.size = 0; } @@ -2484,7 +2487,9 @@ private void grow() { * @see #sealed */ private synchronized void init(final Properties props) { - initLogRecords(0); //Ensure non-null even on exception. + //Ensure non-null even on exception. + //Zero value allows publish to not check for this-escape. + initLogRecords(0); LogManagerProperties.checkLogManagerAccess(); final String p = getClass().getName(); @@ -3097,7 +3102,6 @@ private void initFormatter(final String name) { if (hasValue(name)) { final Formatter f = LogManagerProperties.newFormatter(name); - assert f != null; if (f instanceof TailNameFormatter == false) { formatter = f; } else { @@ -3319,7 +3323,7 @@ private void push(final boolean priority, final int code) { releaseMutex(); } } else { - reportUnPublishedError(null); + reportUnPublishedError((LogRecord) null); } } @@ -3889,6 +3893,8 @@ private void saveChangesNoContent(final Message abort, final String msg) { if (abort != null) { try { try { + //The abort message is non-null at this point so if any + //NPE is thrown then it was due to the call to saveChanges. abort.saveChanges(); } catch (final NullPointerException xferEncoding) { //Workaround GNU JavaMail bug in MimeUtility.getEncoding @@ -4221,7 +4227,7 @@ private String descriptionFrom(Formatter f, Filter filter, Formatter name) { */ private String getClassId(final Formatter f) { if (f == null) { - return "null"; + return "no formatter"; } if (f instanceof TailNameFormatter) { @@ -4362,7 +4368,8 @@ private void appendContentLang(final MimePart p, final Locale l) { try { String lang = LogManagerProperties.toLanguageTag(l); if (lang.length() != 0) { - String header = p.getHeader("Content-Language", null); + String header = p.getHeader("Content-Language", + (String) null); if (isEmpty(header)) { p.setHeader("Content-Language", lang); } else if (!header.equalsIgnoreCase(lang)) { From 8f5366c9ae5a3d28dff2264176e8a3f1f093fd79 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Tue, 30 Jan 2024 22:50:07 -0600 Subject: [PATCH 07/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- doc/src/main/resources/docs/CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/main/resources/docs/CHANGES.txt b/doc/src/main/resources/docs/CHANGES.txt index 3b76bea..e3e8b28 100644 --- a/doc/src/main/resources/docs/CHANGES.txt +++ b/doc/src/main/resources/docs/CHANGES.txt @@ -16,7 +16,7 @@ The following bugs have been fixed in the 2.0.3 release. 107: java.io.UnsupportedEncodingException: en_US.iso885915 if charset is "en_US.iso885915" 110: WildFly support for MailHandler 116: MailHandler LogManger support for mail entries -123: MailHander should catch ServiceConfigurationError +123: MailHandler should catch ServiceConfigurationError CHANGES IN THE 2.0.2 RELEASE ---------------------------- From 255174088b24cbfb610305225aa0ad6703ae5969 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Wed, 31 Jan 2024 13:35:19 -0600 Subject: [PATCH 08/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../angus/mail/util/logging/MailHandler.java | 3 + .../mail/util/logging/MailHandlerTest.java | 139 ++++++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java index 46967cf..915e137 100644 --- a/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java +++ b/mailhandler/src/main/java/org/eclipse/angus/mail/util/logging/MailHandler.java @@ -649,6 +649,9 @@ public MailHandler(Properties props) { */ @Override public boolean isLoggable(final LogRecord record) { + //For a this-escape, level will be off. + //Unless subclass is known, the filter will be null + //and the attachment filters will be an empty array. if (record == null) { //JDK-8233979 return false; } diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java index c5bfd04..7236a04 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java @@ -1533,6 +1533,145 @@ protected void error(MimeMessage msg, Throwable t, int code) { instance.close(); } + private static final class ExploitHandler extends MailHandler { + private static ExploitHandler INSTANCE; + + public static ExploitHandler getInstance() throws Exception { + final Class lock = ExploitHandler.class; + synchronized (lock) { + try { + new ExploitHandler(); + } catch (Throwable ignore) { + } + + for (int i = 0; i < 100; ++i) { + if (INSTANCE == null) { + System.gc(); + System.runFinalization(); + lock.wait(10); + } else { + break; + } + } + + Assume.assumeNotNull(INSTANCE); + return INSTANCE; + } + } + + @Override + public String getEncoding(){ + throw new Error(); + } + + @Override + public Level getLevel() { + return Level.ALL; + } + + @Override + public boolean isLoggable(LogRecord record) { + assertEquals(Level.OFF, super.getLevel()); + assertNull(super.getFilter()); + assertEquals(0, super.getAttachmentFilters().length); + return true; + } + + @Deprecated + @SuppressWarnings("override") + protected void finalize() throws Throwable { + final Class lock = ExploitHandler.class; + synchronized (lock) { + INSTANCE = this; + lock.notify(); + } + } + } + + @Test + public void testThisExcape() throws Exception { + final String p = ExploitHandler.class.getName(); + final LogManager manager = LogManager.getLogManager(); + final Properties props = createInitProperties(p); + props.put(p.concat(".authenticator"), "password"); + props.setProperty(p.concat(".verify"), "local"); + + read(manager, props); + try { + MailHandler h = ExploitHandler.getInstance(); + try { + Authenticator a = h.getAuthenticator(); + fail(String.valueOf(a)); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setAuthenticator((Authenticator) null); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setAuthenticator(new char[0]); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setAuthentication((String) null); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + Properties mail = h.getMailProperties(); + fail(mail.toString()); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setMailProperties(new Properties()); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + ErrorManager em = h.getErrorManager(); + fail(String.valueOf(em)); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setCapacity(1); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + for (int i = 0; i < 5000; ++i) { + LogRecord r = new LogRecord(Level.SEVERE, ""); + assertTrue(h.isLoggable(r)); + h.publish(r); + } + + try { + h.close(); + fail(); + } catch(SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + } finally { + manager.reset(); + } + } + @Test public void testErrorManager() { MailHandler h = new MailHandler(); From cdb2b71dada6fe7b65583d8160b918533b562336 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Wed, 31 Jan 2024 14:55:14 -0600 Subject: [PATCH 09/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../mail/util/logging/MailHandlerTest.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java index 7236a04..67e88d8 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java @@ -1561,11 +1561,19 @@ public static ExploitHandler getInstance() throws Exception { @Override public String getEncoding(){ - throw new Error(); + final Class lock = ExploitHandler.class; + synchronized (lock) { + if (INSTANCE == null) { + throw new Error(); + } else { + return null; + } + } } @Override public Level getLevel() { + assertEquals(Level.OFF, super.getLevel()); return Level.ALL; } @@ -1577,6 +1585,11 @@ public boolean isLoggable(LogRecord record) { return true; } + @Override + protected void reportError(String msg, Exception ex, int code) { + throw new AssertionError(msg + " code: "+ code, ex); + } + @Deprecated @SuppressWarnings("override") protected void finalize() throws Throwable { @@ -1640,7 +1653,7 @@ public void testThisExcape() throws Exception { } catch (SecurityException expect) { assertEquals("this-escape", expect.getMessage()); } - + try { ErrorManager em = h.getErrorManager(); fail(String.valueOf(em)); @@ -1655,9 +1668,11 @@ public void testThisExcape() throws Exception { assertEquals("this-escape", expect.getMessage()); } - for (int i = 0; i < 5000; ++i) { + //Check that p + for (int i = 0; i < 2500; ++i) { LogRecord r = new LogRecord(Level.SEVERE, ""); - assertTrue(h.isLoggable(r)); + assertTrue(h.getClass().getName(), + h.isLoggable(r)); h.publish(r); } From f2b62ed481870112a4eef7090bf9a811dccef40f Mon Sep 17 00:00:00 2001 From: jmehrens Date: Wed, 31 Jan 2024 20:13:08 -0600 Subject: [PATCH 10/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../mail/util/logging/MailHandlerTest.java | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java index 67e88d8..c85ef63 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java @@ -79,6 +79,7 @@ import java.util.Random; import java.util.ResourceBundle; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.logging.ConsoleHandler; @@ -1533,15 +1534,16 @@ protected void error(MimeMessage msg, Throwable t, int code) { instance.close(); } - private static final class ExploitHandler extends MailHandler { - private static ExploitHandler INSTANCE; + private static final class FinalizerHandler extends MailHandler { + private static FinalizerHandler INSTANCE; - public static ExploitHandler getInstance() throws Exception { - final Class lock = ExploitHandler.class; + public static FinalizerHandler getInstance() throws Exception { + final Class lock = FinalizerHandler.class; synchronized (lock) { try { - new ExploitHandler(); - } catch (Throwable ignore) { + FinalizerHandler fh = new FinalizerHandler(); + throw new AssertionError(fh); + } catch (UnknownError ignore) { } for (int i = 0; i < 100; ++i) { @@ -1561,10 +1563,10 @@ public static ExploitHandler getInstance() throws Exception { @Override public String getEncoding(){ - final Class lock = ExploitHandler.class; + final Class lock = FinalizerHandler.class; synchronized (lock) { if (INSTANCE == null) { - throw new Error(); + throw new UnknownError(); } else { return null; } @@ -1593,7 +1595,7 @@ protected void reportError(String msg, Exception ex, int code) { @Deprecated @SuppressWarnings("override") protected void finalize() throws Throwable { - final Class lock = ExploitHandler.class; + final Class lock = FinalizerHandler.class; synchronized (lock) { INSTANCE = this; lock.notify(); @@ -1601,17 +1603,41 @@ protected void finalize() throws Throwable { } } + public static final class FinalizerErrorManager extends ErrorManager { + + private static final InternalErrorManager INSTANCE = new InternalErrorManager(); + private static final AtomicInteger INSTANCES = new AtomicInteger(); + + public synchronized static InternalErrorManager getInstance() { + return INSTANCE; + } + + public static int seen() { + return INSTANCES.get(); + } + + public FinalizerErrorManager() { + INSTANCES.getAndIncrement(); + } + + @Override + public void error(String msg, Exception ex, int code) { + INSTANCE.error(msg, ex, code); + } + } + @Test - public void testThisExcape() throws Exception { - final String p = ExploitHandler.class.getName(); + public void testThisExcapeViaFinalizer() throws Exception { + final String p = FinalizerHandler.class.getName(); final LogManager manager = LogManager.getLogManager(); final Properties props = createInitProperties(p); + props.put(p.concat(".errorManager"), FinalizerErrorManager.class.getName()); props.put(p.concat(".authenticator"), "password"); props.setProperty(p.concat(".verify"), "local"); read(manager, props); try { - MailHandler h = ExploitHandler.getInstance(); + MailHandler h = FinalizerHandler.getInstance(); try { Authenticator a = h.getAuthenticator(); fail(String.valueOf(a)); @@ -1668,7 +1694,8 @@ public void testThisExcape() throws Exception { assertEquals("this-escape", expect.getMessage()); } - //Check that p + //Make sure this is not a multiple of 1000 (default capacity) so + //the close method has records to push. for (int i = 0; i < 2500; ++i) { LogRecord r = new LogRecord(Level.SEVERE, ""); assertTrue(h.getClass().getName(), @@ -1679,9 +1706,16 @@ public void testThisExcape() throws Exception { try { h.close(); fail(); - } catch(SecurityException expect) { + } catch (SecurityException expect) { assertEquals("this-escape", expect.getMessage()); } + + InternalErrorManager em = FinalizerErrorManager.getInstance(); + for (Exception exception : em.exceptions) { + dump(exception); + } + assertTrue(em.exceptions.isEmpty()); + assertEquals(1, FinalizerErrorManager.seen()); } finally { manager.reset(); } From b266073709ab5f3c7d67ddf840797942b6b68db8 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Wed, 31 Jan 2024 21:15:25 -0600 Subject: [PATCH 11/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../angus/mail/util/logging/AbstractLogging.java | 5 ++++- .../angus/mail/util/logging/MailHandlerTest.java | 13 +++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/AbstractLogging.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/AbstractLogging.java index 1460034..83b75d1 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/AbstractLogging.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/AbstractLogging.java @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.PrintStream; import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; @@ -56,7 +57,9 @@ abstract class AbstractLogging { */ @SuppressWarnings({"CallToThreadDumpStack", "CallToPrintStackTrace"}) static void dump(final Throwable t) { - t.printStackTrace(); + PrintStream err = System.err; + err.print(AbstractLogging.class.getSimpleName() + ": "); + t.printStackTrace(err); } /** diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java index c85ef63..22bb865 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java @@ -324,15 +324,16 @@ private void testCallingClassLoader(boolean secure, ClassLoader expect) { ((ClassLoaderThread) clt).secure = false; } - assert em != null; + boolean fail = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException == false) { dump(t); - fail(t.toString()); + fail = true; } } assertFalse(em.exceptions.isEmpty()); + assertFalse(fail); } @Test @@ -413,14 +414,16 @@ private void testVerify(boolean secure, ClassLoader expect) throws Exception { manager.reset(); } - assert em != null; + boolean fail = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException == false) { dump(t); + fail = true; } } assertFalse(em.exceptions.isEmpty()); + assertFalse(fail); } @Test @@ -477,14 +480,16 @@ private void testSetMailProperties(boolean secure, ClassLoader expect) throws Ex ((ClassLoaderThread) clt).secure = false; } - assert em != null; + boolean fail = false; for (Exception exception : em.exceptions) { Throwable t = exception; if (t instanceof MessagingException == false) { dump(t); + fail = true; } } assertFalse(em.exceptions.isEmpty()); + assertFalse(fail); } @Test From 0b14617a826ec18949e781503b43ea1494e38cc5 Mon Sep 17 00:00:00 2001 From: jmehrens Date: Thu, 1 Feb 2024 23:31:41 -0600 Subject: [PATCH 12/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../mail/util/logging/MHThisEscapeTest.java | 216 +++++++ .../mail/util/logging/MailHandlerTest.java | 584 +++--------------- 2 files changed, 317 insertions(+), 483 deletions(-) create mode 100644 providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java new file mode 100644 index 0000000..4e0bba4 --- /dev/null +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2024, 2024 Jason Mehrens. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package org.eclipse.angus.mail.util.logging; + +import jakarta.mail.Authenticator; +import org.junit.Assume; +import org.junit.Test; + +import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.ErrorManager; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class MHThisEscapeTest extends AbstractLogging { + + public MHThisEscapeTest() { + } + + private static final class FinalizerHandler extends MailHandler { + + private static FinalizerHandler INSTANCE; + + public static FinalizerHandler getInstance() throws Exception { + final Class lock = FinalizerHandler.class; + synchronized (lock) { + try { + FinalizerHandler fh = new FinalizerHandler(); + throw new AssertionError(fh); + } catch (UnknownError ignore) { + } + + for (int i = 0; i < 100; ++i) { + if (INSTANCE == null) { + System.gc(); + System.runFinalization(); + lock.wait(10); + } else { + break; + } + } + + Assume.assumeNotNull(INSTANCE); + return INSTANCE; + } + } + + @Override + public String getEncoding() { + final Class lock = FinalizerHandler.class; + synchronized (lock) { + if (INSTANCE == null) { + throw new UnknownError(); + } else { + return null; + } + } + } + + @Override + public Level getLevel() { + assertEquals(Level.OFF, super.getLevel()); + return Level.ALL; + } + + @Override + public boolean isLoggable(LogRecord record) { + assertEquals(Level.OFF, super.getLevel()); + assertNull(super.getFilter()); + assertEquals(0, super.getAttachmentFilters().length); + return true; + } + + @Override + protected void reportError(String msg, Exception ex, int code) { + throw new AssertionError(msg + " code: " + code, ex); + } + + @Deprecated + @SuppressWarnings({"override", "FinalizeDoesntCallSuperFinalize", "FinalizeDeclaration"}) + protected void finalize() throws Throwable { + final Class lock = FinalizerHandler.class; + synchronized (lock) { + INSTANCE = this; + lock.notifyAll(); + } + } + } + + public static final class FinalizerErrorManager extends ErrorManager { + + private static final AtomicInteger ACCESSES = new AtomicInteger(); + + public static int seen() { + return ACCESSES.get(); + } + + public FinalizerErrorManager() { + ACCESSES.getAndIncrement(); + } + + @Override + public void error(String msg, Exception ex, int code) { + ACCESSES.getAndIncrement(); + new ErrorManager().error(msg, ex, code); + } + } + + @Test + public void testThisEscapeViaFinalizer() throws Exception { + final String p = FinalizerHandler.class.getName(); + final LogManager manager = LogManager.getLogManager(); + final Properties props = MailHandlerTest.createInitProperties(p); + props.put(p.concat(".errorManager"), + FinalizerErrorManager.class.getName()); + props.put(p.concat(".authenticator"), "password"); + props.setProperty(p.concat(".verify"), "local"); + + read(manager, props); + try { + MailHandler h = FinalizerHandler.getInstance(); + try { + Authenticator a = h.getAuthenticator(); + fail(String.valueOf(a)); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setAuthenticator((Authenticator) null); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setAuthenticator(new char[0]); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setAuthentication((String) null); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + Properties mail = h.getMailProperties(); + fail(mail.toString()); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setMailProperties(new Properties()); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + ErrorManager em = h.getErrorManager(); + fail(String.valueOf(em)); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + try { + h.setCapacity(1); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + + //Make sure this is not a multiple of 1000 (default capacity) so + //the close method has records to push. + for (int i = 0; i < 2500; ++i) { + LogRecord r = new LogRecord(Level.SEVERE, ""); + assertTrue(h.getClass().getName(), + h.isLoggable(r)); + h.publish(r); + } + + try { + h.close(); + fail(); + } catch (SecurityException expect) { + assertEquals("this-escape", expect.getMessage()); + } + assertEquals(1, FinalizerErrorManager.seen()); + } finally { + manager.reset(); + } + } +} diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java index 22bb865..465cecf 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java @@ -53,7 +53,9 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.NotSerializableException; import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; @@ -75,13 +77,14 @@ import java.util.Comparator; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Properties; import java.util.Random; import java.util.ResourceBundle; import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; +import java.util.function.Predicate; import java.util.logging.ConsoleHandler; import java.util.logging.ErrorManager; import java.util.logging.Filter; @@ -94,6 +97,7 @@ import java.util.logging.MemoryHandler; import java.util.logging.SimpleFormatter; import java.util.logging.XMLFormatter; +import static org.eclipse.angus.mail.util.logging.AbstractLogging.dump; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -324,16 +328,7 @@ private void testCallingClassLoader(boolean secure, ClassLoader expect) { ((ClassLoaderThread) clt).secure = false; } - boolean fail = false; - for (Exception exception : em.exceptions) { - Throwable t = exception; - if (t instanceof MessagingException == false) { - dump(t); - fail = true; - } - } - assertFalse(em.exceptions.isEmpty()); - assertFalse(fail); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -414,16 +409,7 @@ private void testVerify(boolean secure, ClassLoader expect) throws Exception { manager.reset(); } - boolean fail = false; - for (Exception exception : em.exceptions) { - Throwable t = exception; - if (t instanceof MessagingException == false) { - dump(t); - fail = true; - } - } - assertFalse(em.exceptions.isEmpty()); - assertFalse(fail); + em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); } @Test @@ -480,29 +466,17 @@ private void testSetMailProperties(boolean secure, ClassLoader expect) throws Ex ((ClassLoaderThread) clt).secure = false; } - boolean fail = false; - for (Exception exception : em.exceptions) { - Throwable t = exception; - if (t instanceof MessagingException == false) { - dump(t); - fail = true; - } - } - assertFalse(em.exceptions.isEmpty()); - assertFalse(fail); + em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); } @Test public void testIsLoggable() { final Level[] lvls = getAllLevels(); - if (lvls.length > 0) { - LogRecord record = new LogRecord(Level.INFO, ""); - for (Level lvl : lvls) { - testLoggable(lvl, null); - testLoggable(lvl, record); - } - } else { - fail("No predefined levels."); + assertNotEquals(0, lvls.length); + LogRecord record = new LogRecord(Level.INFO, ""); + for (Level lvl : lvls) { + testLoggable(lvl, null); + testLoggable(lvl, record); } } @@ -875,7 +849,7 @@ public void testPublishDuringClose() { //ensure one transport error. assertEquals(1, em.exceptions.size()); - assertTrue(em.exceptions.get(0) instanceof MessagingException); + assertNotNull(MessagingException.class.cast(em.exceptions.get(0))); } } @@ -928,7 +902,7 @@ public void testErrorSubjectFormatter() { instance.publish(record); try { instance.push(); - fail("Error didn't escape push."); + fail(); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -940,7 +914,7 @@ public void testErrorSubjectFormatter() { instance.publish(record); try { instance.flush(); - fail("Error didn't escape flush."); + fail(); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -953,7 +927,7 @@ public void testErrorSubjectFormatter() { record = new LogRecord(Level.INFO, ""); try { instance.publish(record); - fail("Error didn't escape publish at full capacity."); + throw new AssertionError("Error didn't escape publish at full capacity."); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -965,7 +939,7 @@ record = new LogRecord(Level.INFO, ""); instance.publish(record); try { instance.close(); - fail("Error didn't escape close."); + throw new AssertionError("Error didn't escape close."); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -1051,7 +1025,7 @@ public void testThrowFormatters() { em.exceptions.get(size - 1) instanceof MessagingException); return; } - fail("No runtime exceptions reported"); + throw new AssertionError("No runtime exceptions reported"); } @Test @@ -1070,7 +1044,7 @@ public void testErrorFormatters() { instance.publish(record); try { instance.close(); - fail("Error was swallowed."); + throw new AssertionError("Error was swallowed."); } catch (Error expect) { if (expect.getClass() != Error.class) { throw expect; @@ -1098,12 +1072,12 @@ public void testErrorFilters() { instance.setLevel(Level.ALL); instance.setFilter(new ErrorFilter()); instance.isLoggable(record); - fail("Doesn't match the memory handler."); + throw new AssertionError("Doesn't match the memory handler."); } catch (Error resultEx) { assertEquals(expectEx.getClass(), resultEx.getClass()); } } else { - fail("Doesn't match the memory handler."); + throw new AssertionError("Doesn't match the memory handler."); } } @@ -1124,7 +1098,7 @@ public void testErrorFilters() { instance.publish(record); try { instance.close(); - fail("Error was swallowed."); + throw new AssertionError("Error was swallowed."); } catch (Error expect) { if (expect.getClass() != Error.class) { throw expect; @@ -1146,15 +1120,7 @@ public void testSingleSortComparator() { } InternalErrorManager em = internalErrorManagerFrom(instance); - boolean failed = false; - for (Throwable t : em.exceptions) { - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -1257,12 +1223,12 @@ public void testThrowFilters() { instance.setLevel(Level.ALL); instance.setFilter(new ThrowFilter()); instance.isLoggable(record); - fail("Doesn't match the memory handler."); + throw new AssertionError("Doesn't match the memory handler."); } catch (RuntimeException resultEx) { assertEquals(expectEx.getClass(), resultEx.getClass()); } } else { - fail("Doesn't match the memory handler."); + throw new AssertionError("Doesn't match the memory handler."); } } @@ -1524,7 +1490,7 @@ protected void error(MimeMessage msg, Throwable t, int code) { assertEquals(manager.getProperty(p.concat(".encoding")), instance.getEncoding()); try { instance.setEncoding("unsupported encoding exception"); - fail("Missing encoding check."); + throw new AssertionError("Missing encoding check."); } catch (UnsupportedEncodingException expect) { } assertEquals(manager.getProperty(p.concat(".encoding")), instance.getEncoding()); @@ -1539,193 +1505,6 @@ protected void error(MimeMessage msg, Throwable t, int code) { instance.close(); } - private static final class FinalizerHandler extends MailHandler { - private static FinalizerHandler INSTANCE; - - public static FinalizerHandler getInstance() throws Exception { - final Class lock = FinalizerHandler.class; - synchronized (lock) { - try { - FinalizerHandler fh = new FinalizerHandler(); - throw new AssertionError(fh); - } catch (UnknownError ignore) { - } - - for (int i = 0; i < 100; ++i) { - if (INSTANCE == null) { - System.gc(); - System.runFinalization(); - lock.wait(10); - } else { - break; - } - } - - Assume.assumeNotNull(INSTANCE); - return INSTANCE; - } - } - - @Override - public String getEncoding(){ - final Class lock = FinalizerHandler.class; - synchronized (lock) { - if (INSTANCE == null) { - throw new UnknownError(); - } else { - return null; - } - } - } - - @Override - public Level getLevel() { - assertEquals(Level.OFF, super.getLevel()); - return Level.ALL; - } - - @Override - public boolean isLoggable(LogRecord record) { - assertEquals(Level.OFF, super.getLevel()); - assertNull(super.getFilter()); - assertEquals(0, super.getAttachmentFilters().length); - return true; - } - - @Override - protected void reportError(String msg, Exception ex, int code) { - throw new AssertionError(msg + " code: "+ code, ex); - } - - @Deprecated - @SuppressWarnings("override") - protected void finalize() throws Throwable { - final Class lock = FinalizerHandler.class; - synchronized (lock) { - INSTANCE = this; - lock.notify(); - } - } - } - - public static final class FinalizerErrorManager extends ErrorManager { - - private static final InternalErrorManager INSTANCE = new InternalErrorManager(); - private static final AtomicInteger INSTANCES = new AtomicInteger(); - - public synchronized static InternalErrorManager getInstance() { - return INSTANCE; - } - - public static int seen() { - return INSTANCES.get(); - } - - public FinalizerErrorManager() { - INSTANCES.getAndIncrement(); - } - - @Override - public void error(String msg, Exception ex, int code) { - INSTANCE.error(msg, ex, code); - } - } - - @Test - public void testThisExcapeViaFinalizer() throws Exception { - final String p = FinalizerHandler.class.getName(); - final LogManager manager = LogManager.getLogManager(); - final Properties props = createInitProperties(p); - props.put(p.concat(".errorManager"), FinalizerErrorManager.class.getName()); - props.put(p.concat(".authenticator"), "password"); - props.setProperty(p.concat(".verify"), "local"); - - read(manager, props); - try { - MailHandler h = FinalizerHandler.getInstance(); - try { - Authenticator a = h.getAuthenticator(); - fail(String.valueOf(a)); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setAuthenticator((Authenticator) null); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setAuthenticator(new char[0]); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setAuthentication((String) null); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - Properties mail = h.getMailProperties(); - fail(mail.toString()); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setMailProperties(new Properties()); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - ErrorManager em = h.getErrorManager(); - fail(String.valueOf(em)); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setCapacity(1); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - //Make sure this is not a multiple of 1000 (default capacity) so - //the close method has records to push. - for (int i = 0; i < 2500; ++i) { - LogRecord r = new LogRecord(Level.SEVERE, ""); - assertTrue(h.getClass().getName(), - h.isLoggable(r)); - h.publish(r); - } - - try { - h.close(); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - InternalErrorManager em = FinalizerErrorManager.getInstance(); - for (Exception exception : em.exceptions) { - dump(exception); - } - assertTrue(em.exceptions.isEmpty()); - assertEquals(1, FinalizerErrorManager.seen()); - } finally { - manager.reset(); - } - } - @Test public void testErrorManager() { MailHandler h = new MailHandler(); @@ -1788,15 +1567,7 @@ public void testStatefulFilter() { } h.close(); assertEquals(MAX_RECORDS, cf.count); - boolean failed = false; - for (Exception exception : em.exceptions) { - if (!isConnectOrTimeout(exception)) { - dump(exception); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -1822,15 +1593,7 @@ public void testStatefulAttachmentFilter() { assertEquals(MAX_RECORDS, negativeOne.count); assertEquals(MAX_RECORDS, one.count); assertEquals(MAX_RECORDS, two.count); - boolean failed = false; - for (Exception exception : em.exceptions) { - if (!isConnectOrTimeout(exception)) { - dump(exception); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -1865,15 +1628,7 @@ private void testStatefulAttachmentFilter(boolean clear) { assertEquals(MAX_RECORDS, cf.count); assertEquals(MAX_RECORDS, one.count); - boolean failed = false; - for (Exception exception : em.exceptions) { - if (!isConnectOrTimeout(exception)) { - dump(exception); - failed = false; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -1895,15 +1650,7 @@ public void testStatefulPushFilter() { h.publish(r); h.close(); assertEquals(1, cf.count); - boolean failed = false; - for (Exception exception : em.exceptions) { - if (!isConnectOrTimeout(exception)) { - dump(exception); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } private void testStatefulPushAttachmentFilter(boolean clear) { @@ -1951,15 +1698,7 @@ public boolean isLoggable(LogRecord record) { assertEquals(1, two.count); assertEquals(1, push.count); } - boolean failed = false; - for (Exception exception : em.exceptions) { - if (!isConnectOrTimeout(exception)) { - dump(exception); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -2057,19 +1796,19 @@ public String getTail(Handler h) { private void pushTest(MailHandler h) { try { h.setPushLevel(Level.ALL); - fail("Push level mutable during push"); + throw new AssertionError("Push level mutable during push"); } catch (IllegalStateException expect) { } try { h.setPushFilter((Filter) null); - fail("Push filter mutable during push"); + throw new AssertionError("Push filter mutable during push"); } catch (IllegalStateException expect) { } try { h.setPushFilter(new ErrorFilter()); - fail("Push filter mutable during push"); + throw new AssertionError("Push filter mutable during push"); } catch (IllegalStateException expect) { } @@ -2090,7 +1829,7 @@ public String getHead(Handler h) { Formatter[] f = mh.getAttachmentFormatters(); try { mh.setAttachmentFormatters(f); - fail("Mutable formatter."); + throw new AssertionError("Mutable formatter."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2125,7 +1864,7 @@ private void nameTest(MailHandler h) { Formatter[] f = h.getAttachmentNames(); try { h.setAttachmentNames(f); - fail("Mutable formatter"); + throw new AssertionError("Mutable formatter"); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2137,7 +1876,7 @@ private void nameTest(MailHandler h) { names[i] = f[i].toString(); } h.setAttachmentNames(names); - fail("Mutable names"); + throw new AssertionError("Mutable names"); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2155,7 +1894,7 @@ public String getHead(Handler h) { Filter[] f = mh.getAttachmentFilters(); try { mh.setAttachmentFilters(f); - fail("Mutable filters."); + throw new AssertionError("Mutable filters."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2179,7 +1918,7 @@ public String getHead(Handler h) { Comparator c = mh.getComparator(); try { mh.setComparator(c); - fail("Mutable comparator."); + throw new AssertionError("Mutable comparator."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2208,7 +1947,7 @@ public String getHead(Handler h) { Properties props = mh.getMailProperties(); try { mh.setMailProperties(props); - fail("Mutable props."); + throw new AssertionError("Mutable props."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2237,7 +1976,7 @@ public String getHead(Handler h) { Formatter f = mh.getSubject(); try { mh.setSubject(f); - fail("Mutable subject."); + throw new AssertionError("Mutable subject."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2266,7 +2005,7 @@ public String getHead(Handler h) { Authenticator a = mh.getAuthenticator(); try { mh.setAuthenticator(a); - fail("Mutable Authenticator."); + throw new AssertionError("Mutable Authenticator."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2301,17 +2040,8 @@ public String getTail(Handler h) { } instance.flush(); - boolean failed = false; - for (Exception exception : em.exceptions) { - Throwable t = exception; - if ((t instanceof MessagingException == false) - && (t instanceof IllegalStateException == false)) { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> (e instanceof MessagingException == false) + && (e instanceof IllegalStateException == false)); } @Test @@ -2573,15 +2303,7 @@ private void testCloseContextClassLoader0() { instance.close(); } - boolean failed = false; - for (Exception exception : em.exceptions) { - Throwable t = exception; - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } - assertFalse(failed); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -2696,7 +2418,7 @@ public void testLevel() { assertNotNull(instance.getLevel()); try { instance.setLevel((Level) null); - fail("Null level was allowed"); + throw new AssertionError("Null level was allowed"); } catch (NullPointerException pass) { assertNotNull(instance); } catch (RuntimeException re) { @@ -2732,21 +2454,7 @@ public void testLevelBeforeClose() { instance.setFormatter(new LevelCheckingFormatter(expect)); instance.close(); - boolean failed = false; - for (Exception exception : em.exceptions) { - Throwable t = exception; - if (t instanceof MessagingException) { - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } else { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -2761,21 +2469,7 @@ public void testLevelAfterClose() { assertEquals(Level.OFF, instance.getLevel()); instance.close(); - boolean failed = false; - for (Exception exception : em.exceptions) { - Throwable t = exception; - if (t instanceof MessagingException) { - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } else { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -2855,7 +2549,7 @@ public void testLogManagerReset() throws IOException { for (Exception exception : em.exceptions) { Throwable t = exception; dump(t); - fail("Verify index=" + v); + throw new AssertionError("Verify index=" + v); } manager.reset(); @@ -2869,11 +2563,11 @@ public void testLogManagerReset() throws IOException { } if (!isConnectOrTimeout(t)) { dump(t); - fail("Verify index=" + v); + throw new AssertionError("Verify index=" + v); } } else { dump(t); - fail("Verify index=" + v); + throw new AssertionError("Verify index=" + v); } } } @@ -3450,16 +3144,8 @@ protected void error(MimeMessage msg, Throwable t, int code) { target.close(); - boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); - for (Exception t : em.exceptions) { - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } finally { Locale.setDefault(l); if (!f.delete() && f.exists()) { @@ -3569,16 +3255,8 @@ private void testContentLangInfer(MailHandler target, String logPrefix, String b target.close(); - boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); - for (Exception t : em.exceptions) { - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } @Test @@ -3693,16 +3371,8 @@ protected void error(MimeMessage msg, Throwable t, int code) { target.close(); - boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); - for (Exception t : em.exceptions) { - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !isConnectOrTimeout(e)); } private File testContentLangExact(MailHandler target, Properties props, String exact) throws Exception { @@ -3921,7 +3591,8 @@ public void testCapacity() { assertEquals(1, formatter.head); assertEquals(1, formatter.tail); assertEquals(1, em.exceptions.size()); - assertTrue(em.exceptions.get(0) instanceof MessagingException); + assertTrue(MessagingException.class.isAssignableFrom( + em.exceptions.get(0).getClass())); instance.close(); } } @@ -4107,16 +3778,8 @@ public void testMailProperties() throws Exception { props.setProperty("mail.to", "localhost@localdomain"); instance.setMailProperties(props); instance.flush(); - boolean failed = false; - for (Exception exception : em.exceptions) { - final Throwable t = exception; - if (!isConnectOrTimeout(t)) { - dump(t); - failed = true; - } - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + + em.failWhen(e -> !isConnectOrTimeout(e)); props.setProperty("mail.from", "localhost@localdomain"); props.setProperty("mail.to", "::1@@"); @@ -4127,17 +3790,7 @@ public void testMailProperties() throws Exception { instance.publish(new LogRecord(Level.SEVERE, "test")); instance.close(); - failed = false; - for (Exception exception : em.exceptions) { - final Throwable t = exception; - if (t instanceof AddressException || isConnectOrTimeout(t)) { - continue; - } - dump(t); - failed = true; - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !(e instanceof AddressException || isConnectOrTimeout(e))); } @@ -4159,51 +3812,18 @@ public void testInitMailEntriesNullMailProperties() throws Exception { assertEquals("localhost@localdomain", stored.getProperty("mail.from")); assertEquals("local",stored.getProperty("verify")); - boolean failed = false; - for (Exception e : em.exceptions) { - if (e instanceof AddressException) { - if (e.toString().contains("badAddress")) { - continue; - } - } - dump(e); - failed = true; - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); target.setMailProperties((Properties) null); stored = target.getMailProperties(); assertEquals("localhost@localdomain", stored.getProperty("mail.from")); assertEquals("local", stored.getProperty("verify")); - failed = false; - for (Exception e : em.exceptions) { - if (e instanceof AddressException) { - if (e.toString().contains("badAddress")) { - continue; - } - } - dump(e); - failed = true; - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); target = new MailHandler((Properties) null); em = internalErrorManagerFrom(target); - failed = false; - for (Exception e : em.exceptions) { - if (e instanceof AddressException) { - if (e.toString().contains("badAddress")) { - continue; - } - } - dump(e); - failed = true; - } - assertFalse(failed); - assertFalse(em.exceptions.isEmpty()); + em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); stored = target.getMailProperties(); assertEquals("localhost@localdomain", @@ -4242,17 +3862,8 @@ public void testInitMailEntriesNullSetMailEntries() throws Exception { InternalErrorManager em = internalErrorManagerFrom(target); target.setMailEntries((String) null); Properties stored = target.getMailProperties(); - boolean failed = false; - for (Exception e : em.exceptions) { - if (e instanceof AddressException) { - if (e.toString().contains("badAddress")) { - continue; - } - } - dump(e); - failed = true; - } - assertFalse(failed); + em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); + assertFalse(em.exceptions.isEmpty()); assertEquals("localhost@localdomain", stored.getProperty("mail.from")); @@ -5594,7 +5205,7 @@ public void testVerifyNoContent() throws Exception { msg.saveChanges(); try { msg.writeTo(out); - fail("Verify type 'remote' may send a message with no content."); + throw new AssertionError("Verify type 'remote' may send a message with no content."); } catch (MessagingException | IOException expect) { msg.setContent("", "text/plain"); msg.saveChanges(); @@ -5618,7 +5229,7 @@ public void testIsMissingContent() throws Exception { msg.saveChanges(); try { msg.writeTo(new ByteArrayOutputStream(384)); - fail("Verify type 'remote' may hide remote exceptions."); + throw new AssertionError("Verify type 'remote' may hide remote exceptions."); } catch (RuntimeException re) { throw re; //Avoid catch all. } catch (Exception expect) { @@ -7063,10 +6674,21 @@ static Properties createInitProperties(String p) { if (p.length() != 0) { p = p.concat("."); } - props.put("mail.host", UNKNOWN_HOST); - props.put(p.concat("mail.host"), UNKNOWN_HOST); - props.put(p.concat("mail.smtp.host"), UNKNOWN_HOST); - props.put(p.concat("mail.smtp.port"), Integer.toString(OPEN_PORT)); + + String host = UNKNOWN_HOST; + if (host == null) { + UNKNOWN_HOST = host = findUnknownHost(); + } + + int port = OPEN_PORT; + if (port <= 0) { + OPEN_PORT = port = findOpenPort(); + } + + props.put("mail.host", host); + props.put(p.concat("mail.host"), host); + props.put(p.concat("mail.smtp.host"), host); + props.put(p.concat("mail.smtp.port"), Integer.toString(port)); props.put(p.concat("mail.to"), ""); props.put(p.concat("mail.cc"), "badAddress"); props.put(p.concat("mail.from"), ""); @@ -7121,7 +6743,7 @@ private static String freeTextSubject() { String name = "Mail Handler test subject"; try { Class.forName(name); //ensure this can't be loaded. - fail("Invalid subject: " + name); + throw new AssertionError("Invalid subject: " + name); } catch (AssertionError fail) { throw fail; } catch (Throwable expected) { @@ -7480,7 +7102,7 @@ public final void error(String msg, Exception ex, int code) { } } else { new ErrorManager().error(msg, ex, code); - fail("Message.writeTo failed."); + throw new AssertionError("Message.writeTo failed."); } } @@ -7851,6 +7473,17 @@ public static class InternalErrorManager extends ErrorManager { public void error(String msg, Exception ex, int code) { exceptions.add(ex); } + + public final void failWhen(Predicate p) { + boolean fail = false; + for (Exception exception : exceptions) { + if (p.test(exception)) { + dump(exception); + fail = true; + } + } + assertFalse(fail); + } } public static final class ThrowAuthenticator extends jakarta.mail.Authenticator { @@ -7877,26 +7510,6 @@ public String format(LogRecord r) { } } - public static final class GaeErrorManager extends MessageErrorManager { - - public GaeErrorManager(MailHandler h) { - super(h.getMailProperties()); - } - - @Override - protected void error(MimeMessage message, Throwable t, int code) { - try { - assertFalse(LogManagerProperties.hasLogManager()); - String[] a = message.getHeader("auto-submitted"); - assertTrue(Arrays.toString(a), a == null || a.length == 0); - message.saveChanges(); - } catch (Exception ME) { - dump(ME); - fail(ME.toString()); - } - } - } - public static class ErrorFormatter extends Formatter { @Override @@ -8710,7 +8323,7 @@ public final static class ClassLoaderComparator private static final long serialVersionUID = -1L; - private final ClassLoader expect; + private final transient ClassLoader expect; public ClassLoaderComparator() { this(LOADER.get()); @@ -8743,6 +8356,11 @@ public String toString() { checkContextClassLoader(expect); return super.toString(); } + + private void writeObject(ObjectOutputStream out) throws IOException { + Objects.requireNonNull(out); + throw new NotSerializableException(getClass().getName()); + } } public final static class ClassLoaderFilterFormatter From 0e58177a5a354907b126383e636ead47ae2f3abd Mon Sep 17 00:00:00 2001 From: jmehrens Date: Thu, 1 Feb 2024 23:56:33 -0600 Subject: [PATCH 13/13] MailHander should catch ServiceConfigurationError #123 Signed-off-by: jmehrens --- .../mail/util/logging/MHThisEscapeTest.java | 216 ---------- .../mail/util/logging/MailHandlerTest.java | 391 +++++++++++++----- 2 files changed, 290 insertions(+), 317 deletions(-) delete mode 100644 providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java deleted file mode 100644 index 4e0bba4..0000000 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MHThisEscapeTest.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2024, 2024 Jason Mehrens. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0, which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the - * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, - * version 2 with the GNU Classpath Exception, which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 - */ -package org.eclipse.angus.mail.util.logging; - -import jakarta.mail.Authenticator; -import org.junit.Assume; -import org.junit.Test; - -import java.util.Properties; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.ErrorManager; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.LogRecord; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -public class MHThisEscapeTest extends AbstractLogging { - - public MHThisEscapeTest() { - } - - private static final class FinalizerHandler extends MailHandler { - - private static FinalizerHandler INSTANCE; - - public static FinalizerHandler getInstance() throws Exception { - final Class lock = FinalizerHandler.class; - synchronized (lock) { - try { - FinalizerHandler fh = new FinalizerHandler(); - throw new AssertionError(fh); - } catch (UnknownError ignore) { - } - - for (int i = 0; i < 100; ++i) { - if (INSTANCE == null) { - System.gc(); - System.runFinalization(); - lock.wait(10); - } else { - break; - } - } - - Assume.assumeNotNull(INSTANCE); - return INSTANCE; - } - } - - @Override - public String getEncoding() { - final Class lock = FinalizerHandler.class; - synchronized (lock) { - if (INSTANCE == null) { - throw new UnknownError(); - } else { - return null; - } - } - } - - @Override - public Level getLevel() { - assertEquals(Level.OFF, super.getLevel()); - return Level.ALL; - } - - @Override - public boolean isLoggable(LogRecord record) { - assertEquals(Level.OFF, super.getLevel()); - assertNull(super.getFilter()); - assertEquals(0, super.getAttachmentFilters().length); - return true; - } - - @Override - protected void reportError(String msg, Exception ex, int code) { - throw new AssertionError(msg + " code: " + code, ex); - } - - @Deprecated - @SuppressWarnings({"override", "FinalizeDoesntCallSuperFinalize", "FinalizeDeclaration"}) - protected void finalize() throws Throwable { - final Class lock = FinalizerHandler.class; - synchronized (lock) { - INSTANCE = this; - lock.notifyAll(); - } - } - } - - public static final class FinalizerErrorManager extends ErrorManager { - - private static final AtomicInteger ACCESSES = new AtomicInteger(); - - public static int seen() { - return ACCESSES.get(); - } - - public FinalizerErrorManager() { - ACCESSES.getAndIncrement(); - } - - @Override - public void error(String msg, Exception ex, int code) { - ACCESSES.getAndIncrement(); - new ErrorManager().error(msg, ex, code); - } - } - - @Test - public void testThisEscapeViaFinalizer() throws Exception { - final String p = FinalizerHandler.class.getName(); - final LogManager manager = LogManager.getLogManager(); - final Properties props = MailHandlerTest.createInitProperties(p); - props.put(p.concat(".errorManager"), - FinalizerErrorManager.class.getName()); - props.put(p.concat(".authenticator"), "password"); - props.setProperty(p.concat(".verify"), "local"); - - read(manager, props); - try { - MailHandler h = FinalizerHandler.getInstance(); - try { - Authenticator a = h.getAuthenticator(); - fail(String.valueOf(a)); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setAuthenticator((Authenticator) null); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setAuthenticator(new char[0]); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setAuthentication((String) null); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - Properties mail = h.getMailProperties(); - fail(mail.toString()); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setMailProperties(new Properties()); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - ErrorManager em = h.getErrorManager(); - fail(String.valueOf(em)); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - try { - h.setCapacity(1); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - - //Make sure this is not a multiple of 1000 (default capacity) so - //the close method has records to push. - for (int i = 0; i < 2500; ++i) { - LogRecord r = new LogRecord(Level.SEVERE, ""); - assertTrue(h.getClass().getName(), - h.isLoggable(r)); - h.publish(r); - } - - try { - h.close(); - fail(); - } catch (SecurityException expect) { - assertEquals("this-escape", expect.getMessage()); - } - assertEquals(1, FinalizerErrorManager.seen()); - } finally { - manager.reset(); - } - } -} diff --git a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java index 465cecf..c5bfd04 100644 --- a/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java +++ b/providers/angus-mail/src/test/java/org/eclipse/angus/mail/util/logging/MailHandlerTest.java @@ -53,9 +53,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.NotSerializableException; import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; @@ -77,14 +75,12 @@ import java.util.Comparator; import java.util.List; import java.util.Locale; -import java.util.Objects; import java.util.Properties; import java.util.Random; import java.util.ResourceBundle; import java.util.UUID; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; -import java.util.function.Predicate; import java.util.logging.ConsoleHandler; import java.util.logging.ErrorManager; import java.util.logging.Filter; @@ -97,7 +93,6 @@ import java.util.logging.MemoryHandler; import java.util.logging.SimpleFormatter; import java.util.logging.XMLFormatter; -import static org.eclipse.angus.mail.util.logging.AbstractLogging.dump; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -328,7 +323,15 @@ private void testCallingClassLoader(boolean secure, ClassLoader expect) { ((ClassLoaderThread) clt).secure = false; } - em.failWhen(e -> !isConnectOrTimeout(e)); + assert em != null; + for (Exception exception : em.exceptions) { + Throwable t = exception; + if (t instanceof MessagingException == false) { + dump(t); + fail(t.toString()); + } + } + assertFalse(em.exceptions.isEmpty()); } @Test @@ -409,7 +412,14 @@ private void testVerify(boolean secure, ClassLoader expect) throws Exception { manager.reset(); } - em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); + assert em != null; + for (Exception exception : em.exceptions) { + Throwable t = exception; + if (t instanceof MessagingException == false) { + dump(t); + } + } + assertFalse(em.exceptions.isEmpty()); } @Test @@ -466,17 +476,27 @@ private void testSetMailProperties(boolean secure, ClassLoader expect) throws Ex ((ClassLoaderThread) clt).secure = false; } - em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); + assert em != null; + for (Exception exception : em.exceptions) { + Throwable t = exception; + if (t instanceof MessagingException == false) { + dump(t); + } + } + assertFalse(em.exceptions.isEmpty()); } @Test public void testIsLoggable() { final Level[] lvls = getAllLevels(); - assertNotEquals(0, lvls.length); - LogRecord record = new LogRecord(Level.INFO, ""); - for (Level lvl : lvls) { - testLoggable(lvl, null); - testLoggable(lvl, record); + if (lvls.length > 0) { + LogRecord record = new LogRecord(Level.INFO, ""); + for (Level lvl : lvls) { + testLoggable(lvl, null); + testLoggable(lvl, record); + } + } else { + fail("No predefined levels."); } } @@ -849,7 +869,7 @@ public void testPublishDuringClose() { //ensure one transport error. assertEquals(1, em.exceptions.size()); - assertNotNull(MessagingException.class.cast(em.exceptions.get(0))); + assertTrue(em.exceptions.get(0) instanceof MessagingException); } } @@ -902,7 +922,7 @@ public void testErrorSubjectFormatter() { instance.publish(record); try { instance.push(); - fail(); + fail("Error didn't escape push."); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -914,7 +934,7 @@ public void testErrorSubjectFormatter() { instance.publish(record); try { instance.flush(); - fail(); + fail("Error didn't escape flush."); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -927,7 +947,7 @@ public void testErrorSubjectFormatter() { record = new LogRecord(Level.INFO, ""); try { instance.publish(record); - throw new AssertionError("Error didn't escape publish at full capacity."); + fail("Error didn't escape publish at full capacity."); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -939,7 +959,7 @@ record = new LogRecord(Level.INFO, ""); instance.publish(record); try { instance.close(); - throw new AssertionError("Error didn't escape close."); + fail("Error didn't escape close."); } catch (Error expected) { if (expected.getClass() != Error.class) { throw expected; @@ -1025,7 +1045,7 @@ public void testThrowFormatters() { em.exceptions.get(size - 1) instanceof MessagingException); return; } - throw new AssertionError("No runtime exceptions reported"); + fail("No runtime exceptions reported"); } @Test @@ -1044,7 +1064,7 @@ public void testErrorFormatters() { instance.publish(record); try { instance.close(); - throw new AssertionError("Error was swallowed."); + fail("Error was swallowed."); } catch (Error expect) { if (expect.getClass() != Error.class) { throw expect; @@ -1072,12 +1092,12 @@ public void testErrorFilters() { instance.setLevel(Level.ALL); instance.setFilter(new ErrorFilter()); instance.isLoggable(record); - throw new AssertionError("Doesn't match the memory handler."); + fail("Doesn't match the memory handler."); } catch (Error resultEx) { assertEquals(expectEx.getClass(), resultEx.getClass()); } } else { - throw new AssertionError("Doesn't match the memory handler."); + fail("Doesn't match the memory handler."); } } @@ -1098,7 +1118,7 @@ public void testErrorFilters() { instance.publish(record); try { instance.close(); - throw new AssertionError("Error was swallowed."); + fail("Error was swallowed."); } catch (Error expect) { if (expect.getClass() != Error.class) { throw expect; @@ -1120,7 +1140,15 @@ public void testSingleSortComparator() { } InternalErrorManager em = internalErrorManagerFrom(instance); - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Throwable t : em.exceptions) { + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -1223,12 +1251,12 @@ public void testThrowFilters() { instance.setLevel(Level.ALL); instance.setFilter(new ThrowFilter()); instance.isLoggable(record); - throw new AssertionError("Doesn't match the memory handler."); + fail("Doesn't match the memory handler."); } catch (RuntimeException resultEx) { assertEquals(expectEx.getClass(), resultEx.getClass()); } } else { - throw new AssertionError("Doesn't match the memory handler."); + fail("Doesn't match the memory handler."); } } @@ -1490,7 +1518,7 @@ protected void error(MimeMessage msg, Throwable t, int code) { assertEquals(manager.getProperty(p.concat(".encoding")), instance.getEncoding()); try { instance.setEncoding("unsupported encoding exception"); - throw new AssertionError("Missing encoding check."); + fail("Missing encoding check."); } catch (UnsupportedEncodingException expect) { } assertEquals(manager.getProperty(p.concat(".encoding")), instance.getEncoding()); @@ -1567,7 +1595,15 @@ public void testStatefulFilter() { } h.close(); assertEquals(MAX_RECORDS, cf.count); - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + if (!isConnectOrTimeout(exception)) { + dump(exception); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -1593,7 +1629,15 @@ public void testStatefulAttachmentFilter() { assertEquals(MAX_RECORDS, negativeOne.count); assertEquals(MAX_RECORDS, one.count); assertEquals(MAX_RECORDS, two.count); - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + if (!isConnectOrTimeout(exception)) { + dump(exception); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -1628,7 +1672,15 @@ private void testStatefulAttachmentFilter(boolean clear) { assertEquals(MAX_RECORDS, cf.count); assertEquals(MAX_RECORDS, one.count); - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + if (!isConnectOrTimeout(exception)) { + dump(exception); + failed = false; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -1650,7 +1702,15 @@ public void testStatefulPushFilter() { h.publish(r); h.close(); assertEquals(1, cf.count); - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + if (!isConnectOrTimeout(exception)) { + dump(exception); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } private void testStatefulPushAttachmentFilter(boolean clear) { @@ -1698,7 +1758,15 @@ public boolean isLoggable(LogRecord record) { assertEquals(1, two.count); assertEquals(1, push.count); } - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + if (!isConnectOrTimeout(exception)) { + dump(exception); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -1796,19 +1864,19 @@ public String getTail(Handler h) { private void pushTest(MailHandler h) { try { h.setPushLevel(Level.ALL); - throw new AssertionError("Push level mutable during push"); + fail("Push level mutable during push"); } catch (IllegalStateException expect) { } try { h.setPushFilter((Filter) null); - throw new AssertionError("Push filter mutable during push"); + fail("Push filter mutable during push"); } catch (IllegalStateException expect) { } try { h.setPushFilter(new ErrorFilter()); - throw new AssertionError("Push filter mutable during push"); + fail("Push filter mutable during push"); } catch (IllegalStateException expect) { } @@ -1829,7 +1897,7 @@ public String getHead(Handler h) { Formatter[] f = mh.getAttachmentFormatters(); try { mh.setAttachmentFormatters(f); - throw new AssertionError("Mutable formatter."); + fail("Mutable formatter."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -1864,7 +1932,7 @@ private void nameTest(MailHandler h) { Formatter[] f = h.getAttachmentNames(); try { h.setAttachmentNames(f); - throw new AssertionError("Mutable formatter"); + fail("Mutable formatter"); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -1876,7 +1944,7 @@ private void nameTest(MailHandler h) { names[i] = f[i].toString(); } h.setAttachmentNames(names); - throw new AssertionError("Mutable names"); + fail("Mutable names"); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -1894,7 +1962,7 @@ public String getHead(Handler h) { Filter[] f = mh.getAttachmentFilters(); try { mh.setAttachmentFilters(f); - throw new AssertionError("Mutable filters."); + fail("Mutable filters."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -1918,7 +1986,7 @@ public String getHead(Handler h) { Comparator c = mh.getComparator(); try { mh.setComparator(c); - throw new AssertionError("Mutable comparator."); + fail("Mutable comparator."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -1947,7 +2015,7 @@ public String getHead(Handler h) { Properties props = mh.getMailProperties(); try { mh.setMailProperties(props); - throw new AssertionError("Mutable props."); + fail("Mutable props."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -1976,7 +2044,7 @@ public String getHead(Handler h) { Formatter f = mh.getSubject(); try { mh.setSubject(f); - throw new AssertionError("Mutable subject."); + fail("Mutable subject."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2005,7 +2073,7 @@ public String getHead(Handler h) { Authenticator a = mh.getAuthenticator(); try { mh.setAuthenticator(a); - throw new AssertionError("Mutable Authenticator."); + fail("Mutable Authenticator."); } catch (IllegalStateException pass) { } catch (Throwable T) { fail(T.toString()); @@ -2040,8 +2108,17 @@ public String getTail(Handler h) { } instance.flush(); - em.failWhen(e -> (e instanceof MessagingException == false) - && (e instanceof IllegalStateException == false)); + boolean failed = false; + for (Exception exception : em.exceptions) { + Throwable t = exception; + if ((t instanceof MessagingException == false) + && (t instanceof IllegalStateException == false)) { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -2303,7 +2380,15 @@ private void testCloseContextClassLoader0() { instance.close(); } - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + Throwable t = exception; + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } + assertFalse(failed); } @Test @@ -2418,7 +2503,7 @@ public void testLevel() { assertNotNull(instance.getLevel()); try { instance.setLevel((Level) null); - throw new AssertionError("Null level was allowed"); + fail("Null level was allowed"); } catch (NullPointerException pass) { assertNotNull(instance); } catch (RuntimeException re) { @@ -2454,7 +2539,21 @@ public void testLevelBeforeClose() { instance.setFormatter(new LevelCheckingFormatter(expect)); instance.close(); - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + Throwable t = exception; + if (t instanceof MessagingException) { + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } else { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -2469,7 +2568,21 @@ public void testLevelAfterClose() { assertEquals(Level.OFF, instance.getLevel()); instance.close(); - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + Throwable t = exception; + if (t instanceof MessagingException) { + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } else { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -2549,7 +2662,7 @@ public void testLogManagerReset() throws IOException { for (Exception exception : em.exceptions) { Throwable t = exception; dump(t); - throw new AssertionError("Verify index=" + v); + fail("Verify index=" + v); } manager.reset(); @@ -2563,11 +2676,11 @@ public void testLogManagerReset() throws IOException { } if (!isConnectOrTimeout(t)) { dump(t); - throw new AssertionError("Verify index=" + v); + fail("Verify index=" + v); } } else { dump(t); - throw new AssertionError("Verify index=" + v); + fail("Verify index=" + v); } } } @@ -3144,8 +3257,16 @@ protected void error(MimeMessage msg, Throwable t, int code) { target.close(); + boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); - em.failWhen(e -> !isConnectOrTimeout(e)); + for (Exception t : em.exceptions) { + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } finally { Locale.setDefault(l); if (!f.delete() && f.exists()) { @@ -3255,8 +3376,16 @@ private void testContentLangInfer(MailHandler target, String logPrefix, String b target.close(); + boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); - em.failWhen(e -> !isConnectOrTimeout(e)); + for (Exception t : em.exceptions) { + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @Test @@ -3371,8 +3500,16 @@ protected void error(MimeMessage msg, Throwable t, int code) { target.close(); + boolean failed = false; InternalErrorManager em = internalErrorManagerFrom(target); - em.failWhen(e -> !isConnectOrTimeout(e)); + for (Exception t : em.exceptions) { + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } private File testContentLangExact(MailHandler target, Properties props, String exact) throws Exception { @@ -3591,8 +3728,7 @@ public void testCapacity() { assertEquals(1, formatter.head); assertEquals(1, formatter.tail); assertEquals(1, em.exceptions.size()); - assertTrue(MessagingException.class.isAssignableFrom( - em.exceptions.get(0).getClass())); + assertTrue(em.exceptions.get(0) instanceof MessagingException); instance.close(); } } @@ -3778,8 +3914,16 @@ public void testMailProperties() throws Exception { props.setProperty("mail.to", "localhost@localdomain"); instance.setMailProperties(props); instance.flush(); - - em.failWhen(e -> !isConnectOrTimeout(e)); + boolean failed = false; + for (Exception exception : em.exceptions) { + final Throwable t = exception; + if (!isConnectOrTimeout(t)) { + dump(t); + failed = true; + } + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); props.setProperty("mail.from", "localhost@localdomain"); props.setProperty("mail.to", "::1@@"); @@ -3790,7 +3934,17 @@ public void testMailProperties() throws Exception { instance.publish(new LogRecord(Level.SEVERE, "test")); instance.close(); - em.failWhen(e -> !(e instanceof AddressException || isConnectOrTimeout(e))); + failed = false; + for (Exception exception : em.exceptions) { + final Throwable t = exception; + if (t instanceof AddressException || isConnectOrTimeout(t)) { + continue; + } + dump(t); + failed = true; + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); } @@ -3812,18 +3966,51 @@ public void testInitMailEntriesNullMailProperties() throws Exception { assertEquals("localhost@localdomain", stored.getProperty("mail.from")); assertEquals("local",stored.getProperty("verify")); - em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); + boolean failed = false; + for (Exception e : em.exceptions) { + if (e instanceof AddressException) { + if (e.toString().contains("badAddress")) { + continue; + } + } + dump(e); + failed = true; + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); target.setMailProperties((Properties) null); stored = target.getMailProperties(); assertEquals("localhost@localdomain", stored.getProperty("mail.from")); assertEquals("local", stored.getProperty("verify")); - em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); + failed = false; + for (Exception e : em.exceptions) { + if (e instanceof AddressException) { + if (e.toString().contains("badAddress")) { + continue; + } + } + dump(e); + failed = true; + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); target = new MailHandler((Properties) null); em = internalErrorManagerFrom(target); - em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); + failed = false; + for (Exception e : em.exceptions) { + if (e instanceof AddressException) { + if (e.toString().contains("badAddress")) { + continue; + } + } + dump(e); + failed = true; + } + assertFalse(failed); + assertFalse(em.exceptions.isEmpty()); stored = target.getMailProperties(); assertEquals("localhost@localdomain", @@ -3862,8 +4049,17 @@ public void testInitMailEntriesNullSetMailEntries() throws Exception { InternalErrorManager em = internalErrorManagerFrom(target); target.setMailEntries((String) null); Properties stored = target.getMailProperties(); - em.failWhen(e -> !(e instanceof AddressException && e.toString().contains("badAddress"))); - + boolean failed = false; + for (Exception e : em.exceptions) { + if (e instanceof AddressException) { + if (e.toString().contains("badAddress")) { + continue; + } + } + dump(e); + failed = true; + } + assertFalse(failed); assertFalse(em.exceptions.isEmpty()); assertEquals("localhost@localdomain", stored.getProperty("mail.from")); @@ -5205,7 +5401,7 @@ public void testVerifyNoContent() throws Exception { msg.saveChanges(); try { msg.writeTo(out); - throw new AssertionError("Verify type 'remote' may send a message with no content."); + fail("Verify type 'remote' may send a message with no content."); } catch (MessagingException | IOException expect) { msg.setContent("", "text/plain"); msg.saveChanges(); @@ -5229,7 +5425,7 @@ public void testIsMissingContent() throws Exception { msg.saveChanges(); try { msg.writeTo(new ByteArrayOutputStream(384)); - throw new AssertionError("Verify type 'remote' may hide remote exceptions."); + fail("Verify type 'remote' may hide remote exceptions."); } catch (RuntimeException re) { throw re; //Avoid catch all. } catch (Exception expect) { @@ -6674,21 +6870,10 @@ static Properties createInitProperties(String p) { if (p.length() != 0) { p = p.concat("."); } - - String host = UNKNOWN_HOST; - if (host == null) { - UNKNOWN_HOST = host = findUnknownHost(); - } - - int port = OPEN_PORT; - if (port <= 0) { - OPEN_PORT = port = findOpenPort(); - } - - props.put("mail.host", host); - props.put(p.concat("mail.host"), host); - props.put(p.concat("mail.smtp.host"), host); - props.put(p.concat("mail.smtp.port"), Integer.toString(port)); + props.put("mail.host", UNKNOWN_HOST); + props.put(p.concat("mail.host"), UNKNOWN_HOST); + props.put(p.concat("mail.smtp.host"), UNKNOWN_HOST); + props.put(p.concat("mail.smtp.port"), Integer.toString(OPEN_PORT)); props.put(p.concat("mail.to"), ""); props.put(p.concat("mail.cc"), "badAddress"); props.put(p.concat("mail.from"), ""); @@ -6743,7 +6928,7 @@ private static String freeTextSubject() { String name = "Mail Handler test subject"; try { Class.forName(name); //ensure this can't be loaded. - throw new AssertionError("Invalid subject: " + name); + fail("Invalid subject: " + name); } catch (AssertionError fail) { throw fail; } catch (Throwable expected) { @@ -7102,7 +7287,7 @@ public final void error(String msg, Exception ex, int code) { } } else { new ErrorManager().error(msg, ex, code); - throw new AssertionError("Message.writeTo failed."); + fail("Message.writeTo failed."); } } @@ -7473,17 +7658,6 @@ public static class InternalErrorManager extends ErrorManager { public void error(String msg, Exception ex, int code) { exceptions.add(ex); } - - public final void failWhen(Predicate p) { - boolean fail = false; - for (Exception exception : exceptions) { - if (p.test(exception)) { - dump(exception); - fail = true; - } - } - assertFalse(fail); - } } public static final class ThrowAuthenticator extends jakarta.mail.Authenticator { @@ -7510,6 +7684,26 @@ public String format(LogRecord r) { } } + public static final class GaeErrorManager extends MessageErrorManager { + + public GaeErrorManager(MailHandler h) { + super(h.getMailProperties()); + } + + @Override + protected void error(MimeMessage message, Throwable t, int code) { + try { + assertFalse(LogManagerProperties.hasLogManager()); + String[] a = message.getHeader("auto-submitted"); + assertTrue(Arrays.toString(a), a == null || a.length == 0); + message.saveChanges(); + } catch (Exception ME) { + dump(ME); + fail(ME.toString()); + } + } + } + public static class ErrorFormatter extends Formatter { @Override @@ -8323,7 +8517,7 @@ public final static class ClassLoaderComparator private static final long serialVersionUID = -1L; - private final transient ClassLoader expect; + private final ClassLoader expect; public ClassLoaderComparator() { this(LOADER.get()); @@ -8356,11 +8550,6 @@ public String toString() { checkContextClassLoader(expect); return super.toString(); } - - private void writeObject(ObjectOutputStream out) throws IOException { - Objects.requireNonNull(out); - throw new NotSerializableException(getClass().getName()); - } } public final static class ClassLoaderFilterFormatter