Skip to content

Commit

Permalink
Merge pull request #289 from dmlloyd/enc
Browse files Browse the repository at this point in the history
[LOGMGR-267] Fix problems relating to handler encoding
  • Loading branch information
jamezp authored Mar 24, 2020
2 parents 37a099f + 04acfb0 commit 76b49c9
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 29 deletions.
63 changes: 61 additions & 2 deletions core/src/main/java/org/jboss/logmanager/ExtHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -36,7 +35,6 @@
public class OutputStreamHandler extends WriterHandler {

private OutputStream outputStream;
private Charset charset;

/**
* Construct a new instance with no formatter.
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,16 @@
public class WriterHandler extends ExtHandler {

protected final Object outputLock = new Object();
private volatile boolean checkHeadEncoding = true;
private volatile boolean checkTailEncoding = true;
private Writer writer;

/**
* Construct a new instance.
*/
public WriterHandler() {
}

/** {@inheritDoc} */
protected void doPublish(final ExtLogRecord record) {
final String formatted;
Expand Down Expand Up @@ -112,10 +120,59 @@ public void setWriter(final Writer writer) {
}
}

/**
* Determine whether head encoding checking is turned on.
*
* @return {@code true} to check and report head encoding problems, or {@code false} to ignore them
*/
public boolean isCheckHeadEncoding() {
return checkHeadEncoding;
}

/**
* Establish whether head encoding checking is turned on.
*
* @param checkHeadEncoding {@code true} to check and report head encoding problems, or {@code false} to ignore them
* @return this handler
*/
public WriterHandler setCheckHeadEncoding(boolean checkHeadEncoding) {
this.checkHeadEncoding = checkHeadEncoding;
return this;
}

/**
* Determine whether tail encoding checking is turned on.
*
* @return {@code true} to check and report tail encoding problems, or {@code false} to ignore them
*/
public boolean isCheckTailEncoding() {
return checkTailEncoding;
}

/**
* Establish whether tail encoding checking is turned on.
*
* @param checkTailEncoding {@code true} to check and report tail encoding problems, or {@code false} to ignore them
* @return this handler
*/
public WriterHandler setCheckTailEncoding(boolean checkTailEncoding) {
this.checkTailEncoding = checkTailEncoding;
return this;
}

private void writeHead(final Writer writer) {
try {
final Formatter formatter = getFormatter();
if (formatter != null) writer.write(formatter.getHead(this));
if (formatter != null) {
final String head = formatter.getHead(this);
if (checkHeadEncoding) {
if (!getCharset().newEncoder().canEncode(head)) {
reportError("Section header cannot be encoded into charset \"" + getCharset().name() + "\"", null, ErrorManager.GENERIC_FAILURE);
return;
}
}
writer.write(head);
}
} catch (Exception e) {
reportError("Error writing section header", e, ErrorManager.WRITE_FAILURE);
}
Expand All @@ -124,7 +181,16 @@ private void writeHead(final Writer writer) {
private void writeTail(final Writer writer) {
try {
final Formatter formatter = getFormatter();
if (formatter != null) writer.write(formatter.getTail(this));
if (formatter != null) {
final String tail = formatter.getTail(this);
if (checkTailEncoding) {
if (!getCharset().newEncoder().canEncode(tail)) {
reportError("Section tail cannot be encoded into charset \"" + getCharset().name() + "\"", null, ErrorManager.GENERIC_FAILURE);
return;
}
}
writer.write(tail);
}
} catch (Exception ex) {
reportError("Error writing section tail", ex, ErrorManager.WRITE_FAILURE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 76b49c9

Please sign in to comment.