From cd46f32407e495ddfa81982b93232cd7c30db15f Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Wed, 11 Mar 2020 17:47:27 -0500 Subject: [PATCH] Control the encoding uniformly from ExtHandler and never use a null encoding --- .../java/org/jboss/logmanager/ExtHandler.java | 63 ++++++++++++++++++- .../handlers/OutputStreamHandler.java | 35 +++-------- .../ext/handlers/SyslogHandler.java | 2 + 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/jboss/logmanager/ExtHandler.java b/core/src/main/java/org/jboss/logmanager/ExtHandler.java index 20806c43..beb4530b 100644 --- a/core/src/main/java/org/jboss/logmanager/ExtHandler.java +++ b/core/src/main/java/org/jboss/logmanager/ExtHandler.java @@ -21,7 +21,9 @@ import java.io.Flushable; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.security.Permission; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.logging.ErrorManager; import java.util.logging.Filter; @@ -39,11 +41,13 @@ */ public abstract class ExtHandler extends Handler implements AutoCloseable, Flushable { + private static final ErrorManager DEFAULT_ERROR_MANAGER = new OnlyOnceErrorManager(); private static final Permission CONTROL_PERMISSION = new LoggingPermission("control", null); + private volatile boolean autoFlush = true; private volatile boolean enabled = true; private volatile boolean closeChildren; - private static final ErrorManager DEFAULT_ERROR_MANAGER = new OnlyOnceErrorManager(); + private volatile Charset charset = Charset.defaultCharset(); /** * The sub-handlers for this handler. May only be updated using the {@link #handlersUpdater} atomic updater. The array @@ -314,10 +318,65 @@ public void setFilter(final Filter newFilter) throws SecurityException { super.setFilter(newFilter); } + /** + * Set the handler's character set by name. This is roughly equivalent to calling {@link #setCharset(Charset)} with + * the results of {@link Charset#forName(String)}. + * + * @param encoding the name of the encoding + * @throws SecurityException if a security manager is installed and the caller does not have the {@code "control" LoggingPermission} + * @throws UnsupportedEncodingException if no character set could be found for the encoding name + */ @Override public void setEncoding(final String encoding) throws SecurityException, UnsupportedEncodingException { + try { + setCharset(Charset.forName(encoding)); + } catch (IllegalArgumentException e) { + final UnsupportedEncodingException e2 = new UnsupportedEncodingException("Unable to set encoding to \"" + encoding + "\""); + e2.initCause(e); + throw e2; + } + } + + /** + * Get the name of the {@linkplain #getCharset() handler's character set}. + * + * @return the handler character set name + */ + @Override + public String getEncoding() { + return getCharset().name(); + } + + /** + * Set the handler's character set. If not set, the handler's character set is initialized to the platform default + * character set. + * + * @param charset the character set (must not be {@code null}) + * @throws SecurityException if a security manager is installed and the caller does not have the {@code "control" LoggingPermission} + */ + public void setCharset(final Charset charset) throws SecurityException { checkAccess(); - super.setEncoding(encoding); + setCharsetPrivate(charset); + } + + /** + * Set the handler's character set from within this handler. If not set, the handler's character set is initialized + * to the platform default character set. + * + * @param charset the character set (must not be {@code null}) + */ + protected void setCharsetPrivate(final Charset charset) throws SecurityException { + Objects.requireNonNull(charset, "charset"); + this.charset = charset; + } + + /** + * Get the handler's character set. + * + * @return the character set in use (not {@code null}) + */ + public Charset getCharset() { + return charset; } @Override diff --git a/core/src/main/java/org/jboss/logmanager/handlers/OutputStreamHandler.java b/core/src/main/java/org/jboss/logmanager/handlers/OutputStreamHandler.java index 775d5919..96885d05 100644 --- a/core/src/main/java/org/jboss/logmanager/handlers/OutputStreamHandler.java +++ b/core/src/main/java/org/jboss/logmanager/handlers/OutputStreamHandler.java @@ -22,7 +22,6 @@ import org.jboss.logmanager.formatters.Formatters; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; import java.io.Writer; import java.nio.charset.Charset; @@ -36,7 +35,6 @@ public class OutputStreamHandler extends WriterHandler { private OutputStream outputStream; - private Charset charset; /** * Construct a new instance with no formatter. @@ -65,29 +63,11 @@ public OutputStreamHandler(final OutputStream outputStream, final Formatter form setOutputStream(outputStream); } - /** - * Get the target encoding. - * - * @return the target encoding, or {@code null} if the platform default is being used - */ - public String getEncoding() { - synchronized (outputLock) { - return super.getEncoding(); - } - } - - /** - * Set the target encoding. - * - * @param encoding the new encoding - * @throws SecurityException if you do not have sufficient permission to invoke this operation - * @throws java.io.UnsupportedEncodingException if the specified encoding is not supported - */ - public void setEncoding(final String encoding) throws SecurityException, UnsupportedEncodingException { + @Override + protected void setCharsetPrivate(Charset charset) throws SecurityException { // superclass checks access synchronized (outputLock) { - charset = encoding == null ? null : Charset.forName(encoding); - super.setEncoding(encoding); + super.setCharsetPrivate(charset); // we only want to change the writer, not the output stream final OutputStream outputStream = this.outputStream; if (outputStream != null) { @@ -96,6 +76,12 @@ public void setEncoding(final String encoding) throws SecurityException, Unsuppo } } + public Charset getCharset() { + synchronized (outputLock) { + return super.getCharset(); + } + } + /** {@inheritDoc} Setting a writer will replace any target output stream. */ public void setWriter(final Writer writer) { synchronized (outputLock) { @@ -143,7 +129,6 @@ public void setOutputStream(final OutputStream outputStream) { private Writer getNewWriter(OutputStream newOutputStream) { if (newOutputStream == null) return null; final UninterruptibleOutputStream outputStream = new UninterruptibleOutputStream(new UncloseableOutputStream(newOutputStream)); - final Charset charset = this.charset; - return charset == null ? new OutputStreamWriter(outputStream) : new OutputStreamWriter(outputStream, charset); + return new OutputStreamWriter(outputStream, getCharset()); } } diff --git a/ext/src/main/java/org/jboss/logmanager/ext/handlers/SyslogHandler.java b/ext/src/main/java/org/jboss/logmanager/ext/handlers/SyslogHandler.java index 3a1f2281..937a3adb 100644 --- a/ext/src/main/java/org/jboss/logmanager/ext/handlers/SyslogHandler.java +++ b/ext/src/main/java/org/jboss/logmanager/ext/handlers/SyslogHandler.java @@ -25,6 +25,7 @@ import java.io.OutputStream; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.text.DateFormatSymbols; import java.text.Normalizer; import java.text.Normalizer.Form; @@ -462,6 +463,7 @@ public SyslogHandler(final String serverHostname, final int port, final Facility * @throws IOException if an error occurs creating the UDP socket */ public SyslogHandler(final InetAddress serverAddress, final int port, final Facility facility, final SyslogType syslogType, final Protocol protocol, final String hostname) throws IOException { + setCharsetPrivate(StandardCharsets.UTF_8); this.serverAddress = serverAddress; this.port = port; this.facility = facility;