diff --git a/logging/src/main/java/org/xbib/logging/LogContext.java b/logging/src/main/java/org/xbib/logging/LogContext.java index 916a3b2..fab0fbf 100644 --- a/logging/src/main/java/org/xbib/logging/LogContext.java +++ b/logging/src/main/java/org/xbib/logging/LogContext.java @@ -72,7 +72,7 @@ public final class LogContext implements AutoCloseable { } static { - final HashMap> map = new HashMap>(); + final HashMap> map = new HashMap<>(); addStrong(map, Level.OFF); addStrong(map, Level.ALL); addStrong(map, Level.SEVERE); diff --git a/logging/src/main/java/org/xbib/logging/LogManager.java b/logging/src/main/java/org/xbib/logging/LogManager.java index 946e138..ab66a1b 100644 --- a/logging/src/main/java/org/xbib/logging/LogManager.java +++ b/logging/src/main/java/org/xbib/logging/LogManager.java @@ -8,6 +8,7 @@ import java.util.Iterator; import java.util.List; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; import java.util.function.Function; @@ -22,12 +23,17 @@ public final class LogManager extends java.util.logging.LogManager { static { try { // Ensure the StandardOutputStreams are initialized early to capture the current System.out and System.err. - Class.forName(StandardOutputStreams.class.getName()); + ClassLoader.getSystemClassLoader().loadClass(StandardOutputStreams.class.getName()); } catch (ClassNotFoundException ignore) { // ignore } } + private static final AtomicBoolean CONFIGURED = new AtomicBoolean(); + + // Configuration + private final AtomicReference configuratorRef = new AtomicReference<>(); + /** * Construct a new logmanager instance. Attempts to plug a known memory leak in {@link java.util.logging.Level} as * well. @@ -35,24 +41,117 @@ public final class LogManager extends java.util.logging.LogManager { public LogManager() { } - // Configuration - - private final AtomicReference configuratorRef = new AtomicReference<>(); - /** * Configure the system log context initially. */ + @Override public void readConfiguration() { - doConfigure(null); + if (CONFIGURED.compareAndSet(false, true)) { + doConfigure(null); + } } /** - * Configure the system log context initially withe given input stream. + * Configure the system log context initially with a given input stream. * - * @param inputStream ignored + * @param inputStream the input stream */ + @Override public void readConfiguration(InputStream inputStream) { - doConfigure(inputStream); + if (CONFIGURED.compareAndSet(false, true)) { + doConfigure(inputStream); + } + } + + /** + * Does nothing. + * + * @param mapper not used + */ + @Override + public void updateConfiguration(final Function> mapper) throws IOException { + // no operation - the configuration API should be used + } + + /** + * Does nothing. + * + * @param ins not used + * @param mapper not used + */ + @Override + public void updateConfiguration(final InputStream ins, final Function> mapper) + throws IOException { + // no operation - the configuration API should be used + } + + /** + * Configuration listeners are not currently supported. + * + * @param listener not used + * @return this log manager + */ + @Override + public java.util.logging.LogManager addConfigurationListener(final Runnable listener) { + // no operation + return this; + } + + /** + * Configuration listeners are not currently supported. + * + * @param listener not used + */ + @Override + public void removeConfigurationListener(final Runnable listener) { + // no operation + } + + /** + * Does nothing. Properties are not supported. + * + * @param name ignored + * @return {@code null} + */ + @Override + public String getProperty(String name) { + // no properties + return null; + } + + /** + * Does nothing. This method only causes trouble. + */ + @Override + public void reset() { + // no operation! + } + + @Override + public Enumeration getLoggerNames() { + return LogContext.getLogContext().getLoggerNames(); + } + + /** + * Do nothing. Loggers are only added/acquired via {@link #getLogger(String)}. + * + * @param logger ignored + * @return {@code false} + */ + @Override + public boolean addLogger(java.util.logging.Logger logger) { + return false; + } + + /** + * Get or create a logger with the given name. + * + * @param name the logger name + * @return the corresponding logger + */ + @Override + public Logger getLogger(String name) { + return LogContext.getLogContext().getLogger(name); } private void doConfigure(InputStream inputStream) { @@ -100,87 +199,4 @@ public final class LogManager extends java.util.logging.LogManager { } configurator.configure(LogContext.getSystemLogContext(), inputStream); } - - /** - * Does nothing. - * - * @param mapper not used - */ - public void updateConfiguration(final Function> mapper) throws IOException { - // no operation the configuration API should be used - } - - /** - * Does nothing. - * - * @param ins not used - * @param mapper not used - */ - public void updateConfiguration(final InputStream ins, final Function> mapper) - throws IOException { - // no operation the configuration API should be used - } - - /** - * Configuration listeners are not currently supported. - * - * @param listener not used - * @return this log manager - */ - public java.util.logging.LogManager addConfigurationListener(final Runnable listener) { - // no operation - return this; - } - - /** - * Configuration listeners are not currently supported. - * - * @param listener not used - */ - public void removeConfigurationListener(final Runnable listener) { - // no operation - } - - /** - * Does nothing. Properties are not supported. - * - * @param name ignored - * @return {@code null} - */ - public String getProperty(String name) { - // no properties - return null; - } - - /** - * Does nothing. This method only causes trouble. - */ - public void reset() { - // no operation! - } - - @Override - public Enumeration getLoggerNames() { - return LogContext.getLogContext().getLoggerNames(); - } - - /** - * Do nothing. Loggers are only added/acquired via {@link #getLogger(String)}. - * - * @param logger ignored - * @return {@code false} - */ - public boolean addLogger(java.util.logging.Logger logger) { - return false; - } - - /** - * Get or create a logger with the given name. - * - * @param name the logger name - * @return the corresponding logger - */ - public Logger getLogger(String name) { - return LogContext.getLogContext().getLogger(name); - } } diff --git a/logging/src/main/java/org/xbib/logging/LoggerFinder.java b/logging/src/main/java/org/xbib/logging/LoggerFinder.java index 35902de..f329cfc 100644 --- a/logging/src/main/java/org/xbib/logging/LoggerFinder.java +++ b/logging/src/main/java/org/xbib/logging/LoggerFinder.java @@ -47,8 +47,8 @@ public class LoggerFinder extends System.LoggerFinder { final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(name); if (!logger.getClass().getName().equals("org.xbib.logging.Logger")) { if (LOGGED.compareAndSet(false, true)) { - logger.log(Level.ERROR, - "The LogManager accessed before the \"java.util.logging.manager\" system property was set to \"org.xbib.loggingr.LogManager\". Results may be unexpected."); + logger.log(Level.WARN, + "The LogManager accessed before the \"java.util.logging.manager\" system property was set to \"org.xbib.logging.LogManager\". Results may be unexpected."); } } return new SystemLogger(logger); diff --git a/logging/src/main/java/org/xbib/logging/configuration/ObjectBuilder.java b/logging/src/main/java/org/xbib/logging/configuration/ObjectBuilder.java index 4871092..0b50f35 100644 --- a/logging/src/main/java/org/xbib/logging/configuration/ObjectBuilder.java +++ b/logging/src/main/java/org/xbib/logging/configuration/ObjectBuilder.java @@ -26,7 +26,6 @@ class ObjectBuilder { private final Map properties; private final Set definedProperties; private final Set postConstructMethods; - private String moduleName; private ObjectBuilder(final ContextConfiguration contextConfiguration, final Class baseClass, final String className) { @@ -113,7 +112,6 @@ class ObjectBuilder { * @return this builder */ ObjectBuilder setModuleName(final String moduleName) { - this.moduleName = moduleName; return this; } @@ -133,7 +131,7 @@ class ObjectBuilder { final ClassLoader classLoader = getClass().getClassLoader(); final Class actualClass; try { - actualClass = Class.forName(className, true, classLoader).asSubclass(baseClass); + actualClass = classLoader.loadClass(className).asSubclass(baseClass); } catch (Exception e) { throw new IllegalArgumentException(String.format("Failed to load class \"%s\"", className), e); } diff --git a/logging/src/main/java/org/xbib/logging/configuration/PropertyLogContextConfigurator.java b/logging/src/main/java/org/xbib/logging/configuration/PropertyLogContextConfigurator.java index 91ea0a2..d349dfa 100644 --- a/logging/src/main/java/org/xbib/logging/configuration/PropertyLogContextConfigurator.java +++ b/logging/src/main/java/org/xbib/logging/configuration/PropertyLogContextConfigurator.java @@ -13,7 +13,7 @@ import java.util.logging.Logger; import org.xbib.logging.Level; import org.xbib.logging.LogContext; import org.xbib.logging.LogContextConfigurator; -import org.xbib.logging.formatters.PatternFormatter; +import org.xbib.logging.formatters.SimpleFormatter; import org.xbib.logging.handlers.ConsoleHandler; import org.xbib.logging.util.StandardOutputStreams; @@ -60,11 +60,11 @@ public class PropertyLogContextConfigurator implements LogContextConfigurator { final Iterator serviceLoader = ServiceLoader.load(LogContextConfigurator.class, PropertyLogContextConfigurator.class.getClassLoader()).iterator(); if (serviceLoader.hasNext()) { - serviceLoader.next().configure(context, null); + LogContextConfigurator logContextConfigurator = serviceLoader.next(); + logContextConfigurator.configure(context, null); } else { - // Configure a default console handler, pattern formatter and associated with the root logger - final ConsoleHandler handler = - new ConsoleHandler(new PatternFormatter("%d{yyyy-MM-dd'T'HH:mm:ssXXX} %-5p [%c] (%t) %s%e%n")); + // Configure a default console handler, thread formatter and associated with the root logger + final ConsoleHandler handler = new ConsoleHandler(new SimpleFormatter()); handler.setLevel(Level.INFO); handler.setAutoFlush(true); final Logger rootLogger = context.getLogger(""); @@ -75,7 +75,7 @@ public class PropertyLogContextConfigurator implements LogContextConfigurator { } private static InputStream findConfiguration() { - final String propLoc = System.getProperty("logging.configuration"); + final String propLoc = System.getProperty("org.xbib.logging.configuration"); if (propLoc != null) { try { return URI.create(propLoc).toURL().openStream(); @@ -83,10 +83,9 @@ public class PropertyLogContextConfigurator implements LogContextConfigurator { StandardOutputStreams.printError("Unable to read the logging configuration from '%s' (%s)%n", propLoc, e); } } - final ClassLoader cl = PropertyLogContextConfigurator.class.getClassLoader(); try { - return cl.getResourceAsStream("logging.properties"); - } catch (Exception ignore) { + return PropertyLogContextConfigurator.class.getClassLoader().getResourceAsStream("logging.properties"); + } catch (Exception e) { return null; } } diff --git a/logging/src/main/java/org/xbib/logging/formatters/ThreadLoggingFormatter.java b/logging/src/main/java/org/xbib/logging/formatters/SimpleFormatter.java similarity index 91% rename from logging/src/main/java/org/xbib/logging/formatters/ThreadLoggingFormatter.java rename to logging/src/main/java/org/xbib/logging/formatters/SimpleFormatter.java index df3ad3f..75bc625 100644 --- a/logging/src/main/java/org/xbib/logging/formatters/ThreadLoggingFormatter.java +++ b/logging/src/main/java/org/xbib/logging/formatters/SimpleFormatter.java @@ -10,16 +10,16 @@ import java.util.logging.LogManager; import java.util.logging.LogRecord; import org.xbib.logging.util.StackTraceFormatter; -public class ThreadLoggingFormatter extends Formatter { +public class SimpleFormatter extends Formatter { - private static final String PROPERTY_KEY = "org.xbib.interlibrary.jul.ThreadLoggingFormatter.format"; + private static final String PROPERTY_KEY = "org.xbib.logging.formatters.SimpleFormatter.format"; private static final String DEFAULT_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL %4$-7s [%3$s] [%5$s] %6$s %7$s%n"; private final ThreadMXBean threadMXBean; - public ThreadLoggingFormatter() { + public SimpleFormatter() { this.threadMXBean = ManagementFactory.getThreadMXBean(); } @@ -56,7 +56,7 @@ public class ThreadLoggingFormatter extends Formatter { return LogManager.getLogManager().getProperty(name); } - private static final String DEFAULT_FORMAT_STRING = getFormat(ThreadLoggingFormatter::getLoggingProperty); + private static final String DEFAULT_FORMAT_STRING = getFormat(SimpleFormatter::getLoggingProperty); private static String getFormat(Function defaultPropertyGetter) { String format = System.getProperty(PROPERTY_KEY); diff --git a/logging/src/main/java/org/xbib/logging/handlers/ConsoleHandler.java b/logging/src/main/java/org/xbib/logging/handlers/ConsoleHandler.java index f9f0d8b..6f5378f 100644 --- a/logging/src/main/java/org/xbib/logging/handlers/ConsoleHandler.java +++ b/logging/src/main/java/org/xbib/logging/handlers/ConsoleHandler.java @@ -1,14 +1,9 @@ package org.xbib.logging.handlers; import java.io.Console; -import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Writer; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Objects; import java.util.logging.ErrorManager; import java.util.logging.Formatter; import org.xbib.logging.errormanager.HandlerErrorManager; @@ -136,83 +131,6 @@ public class ConsoleHandler extends OutputStreamHandler { private static final String ESC = Character.toString(27); - /** - * Write a PNG image to the console log, if it is supported. - * The image data stream must be closed by the caller. - * - * @param imageData the PNG image data stream to write (must not be {@code null}) - * @param columns the number of text columns to occupy (0 for automatic) - * @param rows the number of text rows to occupy (0 for automatic) - * @return {@code true} if the image was written, or {@code false} if image support isn't found - * @throws IOException if the stream failed while writing the image - */ - public boolean writeImagePng(InputStream imageData, int columns, int rows) throws IOException { - Objects.requireNonNull(imageData, "imageData"); - columns = Math.max(0, columns); - rows = Math.max(0, rows); - if (!isGraphicsSupportPassivelyDetected()) { - // no graphics - return false; - } - lock.lock(); - try { - // clear out any pending stuff - final Writer writer = getWriter(); - if (writer == null) - return false; - // start with the header - try (OutputStream os = Base64.getEncoder().wrap(new OutputStream() { - final byte[] buffer = new byte[2048]; - int pos = 0; - - public void write(final int b) throws IOException { - if (pos == buffer.length) - more(); - buffer[pos++] = (byte) b; - } - - public void write(final byte[] b, int off, int len) throws IOException { - while (len > 0) { - if (pos == buffer.length) { - more(); - } - final int cnt = Math.min(len, buffer.length - pos); - System.arraycopy(b, off, buffer, pos, cnt); - pos += cnt; - off += cnt; - len -= cnt; - } - } - - void more() throws IOException { - writer.write("m=1;"); - writer.write(new String(buffer, 0, pos, StandardCharsets.US_ASCII)); - writer.write(ESC + "\\"); - // set up next segment - writer.write(ESC + "_G"); - pos = 0; - } - - public void close() throws IOException { - writer.write("m=0;"); - writer.write(new String(buffer, 0, pos, StandardCharsets.US_ASCII)); - writer.write(ESC + "\\\n"); - writer.flush(); - pos = 0; - } - })) { - // set the header - writer.write(String.format(ESC + "_Gf=100,a=T,c=%d,r=%d,", Integer.valueOf(columns), Integer.valueOf(rows))); - // write the data in encoded chunks - imageData.transferTo(os); - } - // OK - return true; - } finally { - lock.unlock(); - } - } - /** * Get the local error manager. This is an error manager that will publish errors to this console handler. * The console handler itself should not use this error manager. @@ -264,24 +182,4 @@ public class ConsoleHandler extends OutputStreamHandler { final String colorterm = System.getenv("COLORTERM"); return colorterm != null && (colorterm.contains("truecolor") || colorterm.contains("24bit")); } - - /** - * Determine whether the console can be passively detected to support graphical output. - * This call may be expensive, so the result should be captured for the lifetime of any formatter making use of - * this information. - * - * @return {@code true} if the console exists and supports graphical output; {@code false} otherwise or if - * graphical support cannot be passively detected - */ - public static boolean isGraphicsSupportPassivelyDetected() { - if (!hasConsole()) { - return false; - } - final String term = System.getenv("TERM"); - final String termProgram = System.getenv("TERM_PROGRAM"); - return term != null && (term.equalsIgnoreCase("kitty") - || term.equalsIgnoreCase("xterm-kitty") - || term.equalsIgnoreCase("wezterm") - || term.equalsIgnoreCase("konsole")) || termProgram != null && termProgram.equalsIgnoreCase("wezterm"); - } } diff --git a/logging/src/main/java/org/xbib/logging/handlers/ThreadLoggingHandler.java b/logging/src/main/java/org/xbib/logging/handlers/ThreadLoggingHandler.java deleted file mode 100644 index 0a4672a..0000000 --- a/logging/src/main/java/org/xbib/logging/handlers/ThreadLoggingHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.xbib.logging.handlers; - -import java.util.logging.LogRecord; - -/** - * Replacement for java.util.logging.ConsoleHandler. - */ -public class ThreadLoggingHandler extends ThreadLoggingStreamHandler { - - public ThreadLoggingHandler() { - super(); - this.setOutputStream(System.out); - } - - @Override - public void publish(LogRecord record) { - super.publish(record); - this.flush(); - } - - @Override - public void close() { - this.flush(); - } -} diff --git a/logging/src/main/java/org/xbib/logging/handlers/ThreadLoggingStreamHandler.java b/logging/src/main/java/org/xbib/logging/handlers/ThreadLoggingStreamHandler.java deleted file mode 100644 index a57c4e7..0000000 --- a/logging/src/main/java/org/xbib/logging/handlers/ThreadLoggingStreamHandler.java +++ /dev/null @@ -1,220 +0,0 @@ -package org.xbib.logging.handlers; - -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import org.xbib.logging.formatters.ThreadLoggingFormatter; - -/** - * Replacement for java.util.logging.StreamHandler that is wired to the ThreadLoggingFormatter - * and does not use any fall-back formatter - */ -public class ThreadLoggingStreamHandler extends Handler { - - private static final ThreadLoggingFormatter formatter = new ThreadLoggingFormatter(); - - private final ReentrantLock lock; - - private OutputStream output; - - private boolean doneHeader; - - private volatile Writer writer; - - public ThreadLoggingStreamHandler() { - super(); - this.lock = this.initLocking(); - setLevel(Level.ALL); - setFilter(null); - setFormatter(formatter); - try { - setEncoding("UTF-8"); - } catch (UnsupportedEncodingException e) { - // can't happen - } - } - - protected void setOutputStream(OutputStream out) { - if (this.tryUseLock()) { - try { - this.setOutputStream0(out); - } finally { - this.unlock(); - } - } else { - synchronized (this) { - this.setOutputStream0(out); - } - } - } - - private void setOutputStream0(OutputStream out) { - if (out == null) { - throw new NullPointerException(); - } else { - this.flushAndClose(); - this.output = out; - this.doneHeader = false; - String encoding = this.getEncoding(); - if (encoding == null) { - this.writer = new OutputStreamWriter(this.output); - } else { - try { - this.writer = new OutputStreamWriter(this.output, encoding); - } catch (UnsupportedEncodingException e) { - throw new Error("Unexpected exception " + e); - } - } - } - } - - @Override - public void setEncoding(String encoding) throws UnsupportedEncodingException { - if (this.tryUseLock()) { - try { - this.setEncoding0(encoding); - } finally { - this.unlock(); - } - } else { - synchronized (this) { - this.setEncoding0(encoding); - } - } - } - - private void setEncoding0(String encoding) throws UnsupportedEncodingException { - super.setEncoding(encoding); - if (this.output != null) { - this.flush(); - if (encoding == null) { - this.writer = new OutputStreamWriter(this.output); - } else { - this.writer = new OutputStreamWriter(this.output, encoding); - } - } - } - - @Override - public void publish(LogRecord record) { - if (this.tryUseLock()) { - try { - this.publish0(record); - } finally { - this.unlock(); - } - } else { - synchronized (this) { - this.publish0(record); - } - } - } - - private void publish0(LogRecord record) { - if (this.isLoggable(record)) { - String msg; - try { - msg = this.getFormatter().format(record); - } catch (Exception e) { - this.reportError(null, e, 5); - return; - } - try { - if (!this.doneHeader) { - this.writer.write(this.getFormatter().getHead(this)); - this.doneHeader = true; - } - this.writer.write(msg); - } catch (Exception e) { - this.reportError(null, e, 1); - } - } - } - - @Override - public boolean isLoggable(LogRecord record) { - return this.writer != null && record != null && super.isLoggable(record); - } - - @Override - public void flush() { - if (this.tryUseLock()) { - try { - this.flush0(); - } finally { - this.unlock(); - } - } else { - synchronized (this) { - this.flush0(); - } - } - } - - private void flush0() { - if (this.writer != null) { - try { - this.writer.flush(); - } catch (Exception e) { - this.reportError(null, e, 2); - } - } - } - - private void flushAndClose() { - if (this.writer != null) { - try { - if (!this.doneHeader) { - this.writer.write(this.getFormatter().getHead(this)); - this.doneHeader = true; - } - this.writer.write(this.getFormatter().getTail(this)); - this.writer.flush(); - this.writer.close(); - } catch (Exception e) { - this.reportError(null, e, 3); - } - this.writer = null; - this.output = null; - } - } - - @Override - public void close() { - if (this.tryUseLock()) { - try { - this.flushAndClose(); - } finally { - this.unlock(); - } - } else { - synchronized (this) { - this.flushAndClose(); - } - } - } - - private ReentrantLock initLocking() { - Class clazz = this.getClass(); - ClassLoader loader = clazz.getClassLoader(); - return loader != null && loader != ClassLoader.getPlatformClassLoader() ? null : new ReentrantLock(); - } - - private boolean tryUseLock() { - if (this.lock == null) { - return false; - } else { - this.lock.lock(); - return true; - } - } - - private void unlock() { - this.lock.unlock(); - } -} diff --git a/logging/src/test/java/org/xbib/logging/test/HandlerTests.java b/logging/src/test/java/org/xbib/logging/test/HandlerTests.java index 29f3743..f27b22a 100644 --- a/logging/src/test/java/org/xbib/logging/test/HandlerTests.java +++ b/logging/src/test/java/org/xbib/logging/test/HandlerTests.java @@ -7,6 +7,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.LogRecord; @@ -29,10 +30,10 @@ public final class HandlerTests { @Test public void testNullHandler() { - final ExtHandler handler = new ExtHandler() { - }; - handler.setLevel(Level.ALL); - handler.publish(new ExtLogRecord(Level.INFO, "Test message", null)); + try (final ExtHandler handler = new ExtHandler() {}) { + handler.setLevel(Level.ALL); + handler.publish(new ExtLogRecord(Level.INFO, "Test message", null)); + } } private void initHandler(ExtHandler handler) throws UnsupportedEncodingException { @@ -67,7 +68,7 @@ public final class HandlerTests { final ByteArrayOutputStream stream = new ByteArrayOutputStream(); handler.setOutputStream(stream); testPublish(handler); - assertEquals("Test message", new String(stream.toByteArray(), "utf-8")); + assertEquals("Test message", stream.toString(StandardCharsets.UTF_8)); } @Test @@ -85,20 +86,13 @@ public final class HandlerTests { handler.setFile(tempFile); testPublish(handler); handler.close(); - final ByteArrayOutputStream os = new ByteArrayOutputStream(); - try { - final FileInputStream is = new FileInputStream(tempFile); - try { - int r; - while ((r = is.read()) != -1) - os.write(r); - assertEquals("Test message", new String(os.toByteArray(), "utf-8")); - tempFile.deleteOnExit(); - } finally { - is.close(); - } - } finally { - os.close(); + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); + FileInputStream is = new FileInputStream(tempFile)) { + int r; + while ((r = is.read()) != -1) + os.write(r); + assertEquals("Test message", os.toString(StandardCharsets.UTF_8)); + tempFile.deleteOnExit(); } } finally { tempFile.delete(); @@ -106,7 +100,7 @@ public final class HandlerTests { } @Test - public void testEnableDisableHandler() throws Throwable { + public void testEnableDisableHandler() { final StringListHandler handler = new StringListHandler(); testPublish(handler); assertEquals(1, handler.size());