Skip to content
Commits on Source (5)
jboss-logmanager (2.1.4-1) unstable; urgency=medium
* New upstream version 2.1.4.
* Declare compliance with Debian Policy 4.1.5.
-- Markus Koschany <apo@debian.org> Sat, 07 Jul 2018 22:13:30 +0200
jboss-logmanager (2.1.2-1) unstable; urgency=medium
* New upstream version 2.1.2.
......
......@@ -18,7 +18,7 @@ Build-Depends:
libmaven-source-plugin-java,
libwildfly-common-java,
maven-debian-helper (>= 1.5)
Standards-Version: 4.1.4
Standards-Version: 4.1.5
Vcs-Git: https://anonscm.debian.org/git/pkg-java/jboss-logmanager.git
Vcs-Browser: https://anonscm.debian.org/cgit/pkg-java/jboss-logmanager.git
Homepage: https://github.com/jboss-logging/jboss-logmanager
......
......@@ -3,7 +3,7 @@ Upstream-Name: JBoss Log Manager
Source: https://github.com/jboss-logging/jboss-logmanager
Files: *
Copyright: 2014-2016, Red Hat, Inc.
Copyright: 2014-2018, Red Hat, Inc.
License: Apache-2.0
Files: debian/*
......
......@@ -28,7 +28,7 @@
<groupId>org.jboss.logmanager</groupId>
<artifactId>jboss-logmanager</artifactId>
<packaging>jar</packaging>
<version>2.1.2.Final</version>
<version>2.1.4.Final</version>
<parent>
<groupId>org.jboss</groupId>
......@@ -47,9 +47,9 @@
<properties>
<!-- Dependency versions -->
<version.javax.json>1.0</version.javax.json>
<version.org.byteman>3.0.10</version.org.byteman>
<version.org.byteman>4.0.3</version.org.byteman>
<version.org.glassfish.javax.json>1.0.4</version.org.glassfish.javax.json>
<version.org.jboss.modules.jboss-modules>1.7.0.Beta3</version.org.jboss.modules.jboss-modules>
<version.org.jboss.modules.jboss-modules>1.7.0.Final</version.org.jboss.modules.jboss-modules>
<version.org.wildfly.common.wildfly-common>1.2.0.Final</version.org.wildfly.common.wildfly-common>
<version.junit.junit>4.12</version.junit.junit>
......
......@@ -21,18 +21,20 @@ package org.jboss.logmanager;
import java.lang.ref.WeakReference;
import java.security.Permission;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.logging.Level;
import java.util.logging.LoggingMXBean;
import java.util.logging.LoggingPermission;
......@@ -40,7 +42,8 @@ import java.util.logging.LoggingPermission;
/**
* A logging context, for producing isolated logging environments.
*/
public final class LogContext implements Protectable {
@SuppressWarnings("unused")
public final class LogContext implements Protectable, AutoCloseable {
private static final LogContext SYSTEM_CONTEXT = new LogContext(false);
static final Permission CREATE_CONTEXT_PERMISSION = new RuntimePermission("createLogContext", null);
......@@ -98,6 +101,8 @@ public final class LogContext implements Protectable {
}
private final AtomicReference<Map<String, LevelRef>> levelMapReference;
// Guarded by treeLock
private final Set<AutoCloseable> closeHandlers;
/**
* This lock is taken any time a change is made which affects multiple nodes in the hierarchy.
......@@ -109,6 +114,7 @@ public final class LogContext implements Protectable {
levelMapReference = new AtomicReference<Map<String, LevelRef>>(LazyHolder.INITIAL_LEVEL_MAP);
rootLogger = new LoggerNode(this);
loggerNames = new ConcurrentSkipListMap<String, AtomicInteger>();
closeHandlers = new LinkedHashSet<>();
}
/**
......@@ -340,6 +346,20 @@ public final class LogContext implements Protectable {
granted.remove();
}
@Override
public void close() throws Exception {
synchronized (treeLock) {
// First we want to close all loggers
recursivelyClose(rootLogger);
// Next process the close handlers associated with this log context
for (AutoCloseable handler : closeHandlers) {
handler.close();
}
// Finally clear any logger names
loggerNames.clear();
}
}
/**
* Returns an enumeration of the logger names that have been created. This does not return names of loggers that
* may have been garbage collected. Logger names added after the enumeration has been retrieved may also be added to
......@@ -383,6 +403,49 @@ public final class LogContext implements Protectable {
};
}
/**
* Adds a handler invoked during the {@linkplain #close() close} of this log context. The close handlers will be
* invoked in the order they are added.
* <p>
* The loggers associated with this context will always be closed.
* </p>
*
* @param closeHandler the close handler to use
*/
public void addCloseHandler(final AutoCloseable closeHandler) {
synchronized (treeLock) {
closeHandlers.add(closeHandler);
}
}
/**
* Gets the current close handlers associated with this log context.
*
* @return the current close handlers
*/
public Set<AutoCloseable> getCloseHandlers() {
synchronized (treeLock) {
return new LinkedHashSet<>(closeHandlers);
}
}
/**
* Clears any current close handlers associated with log context, then adds the handlers to be invoked during
* the {@linkplain #close() close} of this log context. The close handlers will be invoked in the order they are
* added.
* <p>
* The loggers associated with this context will always be closed.
* </p>
*
* @param closeHandlers the close handlers to use
*/
public void setCloseHandlers(final Collection<AutoCloseable> closeHandlers) {
synchronized (treeLock) {
this.closeHandlers.clear();
this.closeHandlers.addAll(closeHandlers);
}
}
protected void incrementRef(final String name) {
AtomicInteger counter = loggerNames.get(name);
if (counter == null) {
......@@ -426,6 +489,15 @@ public final class LogContext implements Protectable {
return strong ? new CopyOnWriteMap<String, LoggerNode>() : new CopyOnWriteWeakMap<String, LoggerNode>();
}
private void recursivelyClose(final LoggerNode loggerNode) {
synchronized (treeLock) {
for (LoggerNode child : loggerNode.getChildren()) {
recursivelyClose(child);
}
loggerNode.close();
}
}
private interface LevelRef {
Level get();
}
......
......@@ -24,7 +24,6 @@ import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
......@@ -35,7 +34,7 @@ import java.util.logging.Level;
/**
* A node in the tree of logger names. Maintains weak references to children and a strong reference to its parent.
*/
final class LoggerNode {
final class LoggerNode implements AutoCloseable {
/**
* The log context.
......@@ -144,6 +143,26 @@ final class LoggerNode {
children = context.createChildMap();
}
@Override
public void close() {
synchronized (context.treeLock) {
// Reset everything to defaults
filter = null;
if ("".equals(fullName)) {
level = Level.INFO;
effectiveLevel = Level.INFO.intValue();
} else {
level = null;
effectiveLevel = Level.INFO.intValue();
}
handlersUpdater.clear(this);
useParentFilter = false;
useParentHandlers = true;
attachmentsUpdater.get(this).clear();
children.clear();
}
}
/**
* Get or create a relative logger node. The name is relatively qualified to this node.
*
......@@ -380,13 +399,8 @@ final class LoggerNode {
V old;
do {
oldAttachments = attachments;
if (oldAttachments.isEmpty() || oldAttachments.size() == 1 && oldAttachments.containsKey(key)) {
old = (V) oldAttachments.get(key);
newAttachments = Collections.<Logger.AttachmentKey, Object>singletonMap(key, value);
} else {
newAttachments = new HashMap<Logger.AttachmentKey, Object>(oldAttachments);
newAttachments = new HashMap<>(oldAttachments);
old = (V) newAttachments.put(key, value);
}
} while (! attachmentsUpdater.compareAndSet(this, oldAttachments, newAttachments));
return old;
}
......@@ -403,15 +417,11 @@ final class LoggerNode {
Map<Logger.AttachmentKey, Object> newAttachments;
do {
oldAttachments = attachments;
if (oldAttachments.isEmpty()) {
newAttachments = Collections.<Logger.AttachmentKey, Object>singletonMap(key, value);
} else {
if (oldAttachments.containsKey(key)) {
return (V) oldAttachments.get(key);
}
newAttachments = new HashMap<Logger.AttachmentKey, Object>(oldAttachments);
newAttachments = new HashMap<>(oldAttachments);
newAttachments.put(key, value);
}
} while (! attachmentsUpdater.compareAndSet(this, oldAttachments, newAttachments));
return null;
}
......@@ -434,16 +444,6 @@ final class LoggerNode {
if (size == 1) {
// special case - the new map is empty
newAttachments = Collections.emptyMap();
} else if (size == 2) {
// special case - the new map is a singleton
final Iterator<Map.Entry<Logger.AttachmentKey,Object>> it = oldAttachments.entrySet().iterator();
// find the entry that we are not removing
Map.Entry<Logger.AttachmentKey, Object> entry = it.next();
if (entry.getKey() == key) {
// must be the next one
entry = it.next();
}
newAttachments = Collections.singletonMap(entry.getKey(), entry.getValue());
} else {
newAttachments = new HashMap<Logger.AttachmentKey, Object>(oldAttachments);
}
......
......@@ -85,6 +85,7 @@ final class LogContextConfigurationImpl implements LogContextConfiguration {
LogContextConfigurationImpl(final LogContext logContext) {
this.logContext = logContext;
logContext.addCloseHandler(new LogContextConfigurationCloseHandler());
}
public LogContext getLogContext() {
......@@ -738,4 +739,43 @@ final class LogContextConfigurationImpl implements LogContextConfiguration {
ObjectProducer resolveFilter(String expression) {
return resolveFilter(expression, false);
}
private class LogContextConfigurationCloseHandler implements AutoCloseable {
@Override
public void close() {
final LogContextConfigurationImpl configuration = LogContextConfigurationImpl.this;
// Remove all the loggers first
for (String name : configuration.getLoggerNames()) {
configuration.removeLoggerConfiguration(name);
}
// Remove all handlers next
for (String name : configuration.getHandlerNames()) {
configuration.removeHandlerConfiguration(name);
}
// Remove all filters
for (String name : configuration.getFilterNames()) {
configuration.removeFilterConfiguration(name);
}
// Remove all formatters
for (String name : configuration.getFormatterNames()) {
configuration.removeFormatterConfiguration(name);
}
// Remove all error managers
for (String name : configuration.getErrorManagerNames()) {
configuration.removeErrorManagerConfiguration(name);
}
// Finally remove all POJO's
for (String name : configuration.getPojoNames()) {
configuration.removePojoConfiguration(name);
}
configuration.commit();
}
}
}
......@@ -34,6 +34,7 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
......@@ -1073,11 +1074,15 @@ public final class Formatters {
public static FormatStep mdcFormatStep(final String key, final boolean leftJustify, final int minimumWidth, final boolean truncateBeginning, final int maximumWidth) {
return new JustifyingFormatStep(leftJustify, minimumWidth, truncateBeginning, maximumWidth) {
public void renderRaw(final StringBuilder builder, final ExtLogRecord record) {
if (key == null) {
builder.append(new TreeMap<>(record.getMdcCopy()));
} else {
final String value = record.getMdc(key);
if (value != null) {
builder.append(value);
}
}
}
};
}
......
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.logmanager.handlers;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import javax.net.SocketFactory;
/**
* A factory used to create writable sockets.
*
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
public interface ClientSocketFactory {
/**
* Creates a datagram socket for UDP communication.
*
* @return the newly created socket
*
* @throws SocketException if binding the socket fails
*/
DatagramSocket createDatagramSocket() throws SocketException;
/**
* Creates a TCP socket.
*
* @return the newly created socket
*
* @throws IOException if an error occurs creating the socket
*/
Socket createSocket() throws IOException;
/**
* Returns the address being used to create sockets.
*
* @return the address being used
*/
InetAddress getAddress();
/**
* Returns the port being used to create sockets.
*
* @return the port being used
*/
int getPort();
/**
* A convenience method to return the socket address.
* <p>
* The default implementation simply returns {@code new InetSocketAddress(getAddress(), getPort())}.
* </p>
*
* @return a socket address
*/
default SocketAddress getSocketAddress() {
return new InetSocketAddress(getAddress(), getPort());
}
/**
* Creates a new default implementation of the factory which uses {@link SocketFactory#getDefault()} for TCP
* sockets and {@code new DatagramSocket()} for UDP sockets.
*
* @param address the address to bind to
* @param port the port to bind to
*
* @return the client socket factory
*/
static ClientSocketFactory of(final InetAddress address, final int port) {
return of(SocketFactory.getDefault(), address, port);
}
/**
* Creates a new default implementation of the factory which uses the provided
* {@linkplain SocketFactory#createSocket(InetAddress, int) socket factory} to create TCP connections and
* {@code new DatagramSocket()} for UDP sockets.
*
* @param socketFactory the socket factory used for TCP connections, if {@code null} the
* {@linkplain SocketFactory#getDefault() default} socket factory will be used
* @param address the address to bind to
* @param port the port to bind to
*
* @return the client socket factory
*/
static ClientSocketFactory of(final SocketFactory socketFactory, final InetAddress address, final int port) {
if (address == null || port < 0) {
throw new IllegalArgumentException(String.format("The address cannot be null (%s) and the port must be a positive integer (%d)", address, port));
}
final SocketFactory factory = (socketFactory == null ? SocketFactory.getDefault() : socketFactory);
return new ClientSocketFactory() {
@Override
public DatagramSocket createDatagramSocket() throws SocketException {
return new DatagramSocket();
}
@Override
public Socket createSocket() throws IOException {
return factory.createSocket(address, port);
}
@Override
public InetAddress getAddress() {
return address;
}
@Override
public int getPort() {
return port;
}
};
}
}
......@@ -68,6 +68,7 @@ public class SocketHandler extends ExtHandler {
private final Object outputLock = new Object();
// All the following fields are guarded by outputLock
private ClientSocketFactory clientSocketFactory;
private SocketFactory socketFactory;
private InetAddress address;
private int port;
......@@ -143,6 +144,7 @@ public class SocketHandler extends ExtHandler {
* @param port the port to connect to
*
* @throws UnknownHostException if an error occurs resolving the hostname
* @see #SocketHandler(ClientSocketFactory, Protocol)
*/
public SocketHandler(final SocketFactory socketFactory, final Protocol protocol, final String hostname, final int port) throws UnknownHostException {
this(socketFactory, protocol, InetAddress.getByName(hostname), port);
......@@ -157,14 +159,35 @@ public class SocketHandler extends ExtHandler {
* @param protocol the protocol to connect with
* @param address the address to connect to
* @param port the port to connect to
*
* @see #SocketHandler(ClientSocketFactory, Protocol)
*/
public SocketHandler(final SocketFactory socketFactory, final Protocol protocol, final InetAddress address, final int port) {
this.socketFactory = socketFactory;
this.clientSocketFactory = null;
this.address = address;
this.port = port;
this.protocol = protocol;
this.protocol = (protocol == null ? Protocol.TCP : protocol);
initialize = true;
writer = null;
blockOnReconnect = false;
}
/**
* Creates a socket handler.
*
* @param clientSocketFactory the client socket factory used to create sockets
* @param protocol the protocol to connect with
*/
public SocketHandler(final ClientSocketFactory clientSocketFactory, final Protocol protocol) {
this.clientSocketFactory = clientSocketFactory;
if (clientSocketFactory != null) {
address = clientSocketFactory.getAddress();
port = clientSocketFactory.getPort();
}
this.protocol = (protocol == null ? Protocol.TCP : protocol);
initialize = true;
writer = null;
this.socketFactory = socketFactory;
blockOnReconnect = false;
}
......@@ -229,19 +252,28 @@ public class SocketHandler extends ExtHandler {
/**
* Sets the address to connect to.
* <p>
* Note that is resets the {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param address the address
*/
public void setAddress(final InetAddress address) {
checkAccess(this);
synchronized (outputLock) {
this.address = address;
if (!this.address.equals(address)) {
initialize = true;
clientSocketFactory = null;
}
this.address = address;
}
}
/**
* Sets the address to connect to by doing a lookup on the hostname.
* <p>
* Note that is resets the {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param hostname the host name used to resolve the address
*
......@@ -277,6 +309,7 @@ public class SocketHandler extends ExtHandler {
checkAccess(this);
synchronized (outputLock) {
this.blockOnReconnect = blockOnReconnect;
initialize = true;
}
}
......@@ -293,7 +326,7 @@ public class SocketHandler extends ExtHandler {
* Sets the protocol to use. If the value is {@code null} the protocol will be set to
* {@linkplain Protocol#TCP TCP}.
* <p>
* Note that is resets the {@linkplain #setSocketFactory(SocketFactory) socket factory}.
* Note that is resets the {@linkplain #setSocketFactory(SocketFactory) socket factory} if it was previously set.
* </p>
*
* @param protocol the protocol to use
......@@ -304,11 +337,12 @@ public class SocketHandler extends ExtHandler {
if (protocol == null) {
this.protocol = Protocol.TCP;
}
// Reset the socket factory
if (this.protocol != protocol) {
socketFactory = null;
this.protocol = protocol;
initialize = true;
}
this.protocol = protocol;
}
}
/**
......@@ -322,14 +356,20 @@ public class SocketHandler extends ExtHandler {
/**
* Sets the port to connect to.
* <p>
* Note that is resets the {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param port the port
*/
public void setPort(final int port) {
checkAccess(this);
synchronized (outputLock) {
this.port = port;
if (this.port != port) {
initialize = true;
clientSocketFactory = null;
}
this.port = port;
}
}
......@@ -338,15 +378,33 @@ public class SocketHandler extends ExtHandler {
* connections.
* <p>
* Note that if the {@linkplain #setProtocol(Protocol) protocol} is set the socket factory will be set to
* {@code null} and reset.
* {@code null} and reset. Setting a value here also resets the
* {@linkplain #setClientSocketFactory(ClientSocketFactory) client socket factory}.
* </p>
*
* @param socketFactory the socket factory
*
* @see #setClientSocketFactory(ClientSocketFactory)
*/
public void setSocketFactory(final SocketFactory socketFactory) {
checkAccess(this);
synchronized (outputLock) {
this.socketFactory = socketFactory;
this.clientSocketFactory = null;
initialize = true;
}
}
/**
* Sets the client socket factory used to create sockets. If {@code null} the
* {@linkplain #setAddress(InetAddress) address} and {@linkplain #setPort(int) port} are required to be set.
*
* @param clientSocketFactory the client socket factory to use
*/
public void setClientSocketFactory(final ClientSocketFactory clientSocketFactory) {
checkAccess(this);
synchronized (outputLock) {
this.clientSocketFactory = clientSocketFactory;
initialize = true;
}
}
......@@ -388,24 +446,38 @@ public class SocketHandler extends ExtHandler {
private OutputStream createOutputStream() {
if (address != null || port >= 0) {
try {
final ClientSocketFactory socketFactory = getClientSocketFactory();
if (protocol == Protocol.UDP) {
return new UdpOutputStream(address, port);
return new UdpOutputStream(socketFactory);
}
return new TcpOutputStream(socketFactory, blockOnReconnect);
} catch (IOException e) {
reportError("Failed to create socket output stream", e, ErrorManager.OPEN_FAILURE);
}
}
return null;
}
private ClientSocketFactory getClientSocketFactory() {
synchronized (outputLock) {
if (clientSocketFactory != null) {
return clientSocketFactory;
}
if (address == null || port <= 0) {
throw new IllegalStateException("An address and port greater than 0 is required.");
}
SocketFactory socketFactory = this.socketFactory;
final ClientSocketFactory clientSocketFactory;
if (socketFactory == null) {
if (protocol == Protocol.SSL_TCP) {
this.socketFactory = socketFactory = SSLSocketFactory.getDefault();
clientSocketFactory = ClientSocketFactory.of(SSLSocketFactory.getDefault(), address, port);
} else {
// Assume we want a TCP connection
this.socketFactory = socketFactory = SocketFactory.getDefault();
clientSocketFactory = ClientSocketFactory.of(address, port);
}
} else {
clientSocketFactory = ClientSocketFactory.of(socketFactory, address, port);
}
return new TcpOutputStream(socketFactory, address, port, blockOnReconnect);
} catch (IOException e) {
reportError("Failed to create socket output stream", e, ErrorManager.OPEN_FAILURE);
}
return clientSocketFactory;
}
return null;
}
private void writeHead(final Writer writer) {
......
......@@ -35,6 +35,8 @@ import java.util.logging.ErrorManager;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.regex.Pattern;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import org.jboss.logmanager.ExtHandler;
import org.jboss.logmanager.ExtLogRecord;
......@@ -324,6 +326,7 @@ public class SyslogHandler extends ExtHandler {
private boolean truncate;
private int maxLen;
private boolean blockOnReconnect;
private ClientSocketFactory clientSocketFactory;
/**
* The default class constructor.
......@@ -667,6 +670,19 @@ public class SyslogHandler extends ExtHandler {
}
}
/**
* Sets the client socket factory used to create sockets.
*
* @param clientSocketFactory the client socket factory to use
*/
public void setClientSocketFactory(final ClientSocketFactory clientSocketFactory) {
checkAccess(this);
synchronized (outputLock) {
this.clientSocketFactory = clientSocketFactory;
initializeConnection = true;
}
}
/**
* Checks whether or not characters below decimal 32, traditional US-ASCII control values expect {@code DEL}, are
* being escaped or not.
......@@ -1084,14 +1100,11 @@ public class SyslogHandler extends ExtHandler {
final OutputStream out;
// Check the sockets
try {
if (protocol == Protocol.TCP) {
out = new TcpOutputStream(serverAddress, port, blockOnReconnect);
} else if (protocol == Protocol.UDP) {
out = new UdpOutputStream(serverAddress, port);
} else if (protocol == Protocol.SSL_TCP) {
out = new SslTcpOutputStream(serverAddress, port, blockOnReconnect);
final ClientSocketFactory clientSocketFactory = getClientSocketFactory();
if (protocol == Protocol.UDP) {
out = new UdpOutputStream(clientSocketFactory);
} else {
throw new IllegalStateException("Invalid protocol: " + protocol);
out = new TcpOutputStream(clientSocketFactory, blockOnReconnect);
}
setOutputStream(out, false);
} catch (IOException e) {
......@@ -1296,6 +1309,16 @@ public class SyslogHandler extends ExtHandler {
return buffer.toArray();
}
private ClientSocketFactory getClientSocketFactory() {
synchronized (outputLock) {
if (clientSocketFactory != null) {
return clientSocketFactory;
}
final SocketFactory socketFactory = (protocol == Protocol.SSL_TCP ? SSLSocketFactory.getDefault() : SocketFactory.getDefault());
return ClientSocketFactory.of(socketFactory, serverAddress, port);
}
}
private static String checkPrintableAscii(final String name, final String value) {
if (value != null && PRINTABLE_ASCII_PATTERN.matcher(value).find()) {
final String upper = Character.toUpperCase(name.charAt(0)) + name.substring(1);
......
......@@ -54,9 +54,7 @@ public class TcpOutputStream extends OutputStream implements FlushableCloseable
protected final Object outputLock = new Object();
private final SocketFactory socketFactory;
private final InetAddress address;
private final int port;
private final ClientSocketFactory socketFactory;
private final Deque<Exception> errors = new ArrayDeque<Exception>(maxErrors);
// Guarded by outputLock
......@@ -108,13 +106,11 @@ public class TcpOutputStream extends OutputStream implements FlushableCloseable
*
* @param socket the socket used to write the output to
*
* @deprecated Use {@link #TcpOutputStream(javax.net.SocketFactory, java.net.InetAddress, int)}
* @deprecated Use {@link #TcpOutputStream(ClientSocketFactory, boolean)}
*/
@Deprecated
protected TcpOutputStream(final Socket socket) {
this.socketFactory = null;
this.address = null;
this.port = -1;
this.socket = socket;
reconnectThread = null;
connected = true;
......@@ -152,12 +148,21 @@ public class TcpOutputStream extends OutputStream implements FlushableCloseable
* a reconnect will be attempted on the next write.
*/
protected TcpOutputStream(final SocketFactory socketFactory, final InetAddress address, final int port, final boolean blockOnReconnect) throws IOException {
this(ClientSocketFactory.of(socketFactory, address, port), blockOnReconnect);
}
/**
* Creates a new TCP stream which uses the {@link ClientSocketFactory#createSocket()} to create the socket.
*
* @param socketFactory the socket factory used to create TCP sockets
* @param blockOnReconnect {@code true} to block when attempting to reconnect the socket or {@code false} to
* reconnect asynchronously
*/
public TcpOutputStream(final ClientSocketFactory socketFactory, final boolean blockOnReconnect) {
this.socketFactory = socketFactory;
this.address = address;
this.port = port;
this.blockOnReconnect = blockOnReconnect;
try {
socket = socketFactory.createSocket(address, port);
socket = this.socketFactory.createSocket();
connected = true;
} catch (IOException e) {
connected = false;
......@@ -208,7 +213,9 @@ public class TcpOutputStream extends OutputStream implements FlushableCloseable
public void flush() throws IOException {
synchronized (outputLock) {
try {
if (socket != null) {
socket.getOutputStream().flush();
}
} catch (SocketException e) {
// This should likely never be hit, but should attempt to reconnect if it does happen
if (isReconnectAllowed()) {
......@@ -230,9 +237,11 @@ public class TcpOutputStream extends OutputStream implements FlushableCloseable
if (reconnectThread != null) {
reconnectThread.interrupt();
}
if (socket != null) {
socket.close();
}
}
}
/**
* Indicates whether or not the output stream is set to block when attempting to reconnect a TCP connection.
......@@ -345,7 +354,7 @@ public class TcpOutputStream extends OutputStream implements FlushableCloseable
while (socketFactory != null && !connected) {
Socket socket = null;
try {
socket = socketFactory.createSocket(address, port);
socket = socketFactory.createSocket();
synchronized (outputLock) {
// Unlikely but if we've been interrupted due to a close, we should shutdown
if (Thread.currentThread().isInterrupted()) {
......
......@@ -24,31 +24,39 @@ import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
/**
* An output stream that writes data to a {@link java.net.DatagramSocket DatagramSocket}.
*
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
@SuppressWarnings("WeakerAccess")
public class UdpOutputStream extends OutputStream implements FlushableCloseable {
private final DatagramSocket socket;
private final SocketAddress socketAddress;
public UdpOutputStream(final InetAddress address, final int port) throws IOException {
socket = new DatagramSocket();
socket.connect(address, port);
this(ClientSocketFactory.of(address, port));
}
public UdpOutputStream(final ClientSocketFactory socketManager) throws SocketException {
socket = socketManager.createDatagramSocket();
socketAddress = socketManager.getSocketAddress();
}
@Override
public void write(final int b) throws IOException {
final byte[] msg = new byte[] {(byte) b};
final DatagramPacket packet = new DatagramPacket(msg, 1);
final DatagramPacket packet = new DatagramPacket(msg, 1, socketAddress);
socket.send(packet);
}
@Override
public void write(final byte[] b) throws IOException {
if (b != null) {
final DatagramPacket packet = new DatagramPacket(b, b.length);
final DatagramPacket packet = new DatagramPacket(b, b.length, socketAddress);
socket.send(packet);
}
}
......@@ -56,7 +64,7 @@ public class UdpOutputStream extends OutputStream implements FlushableCloseable
@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
if (b != null) {
final DatagramPacket packet = new DatagramPacket(b, off, len);
final DatagramPacket packet = new DatagramPacket(b, off, len, socketAddress);
socket.send(packet);
}
}
......
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.logmanager;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.UUID;
import java.util.logging.ErrorManager;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.jboss.logmanager.config.ErrorManagerConfiguration;
import org.jboss.logmanager.config.FilterConfiguration;
import org.jboss.logmanager.config.FormatterConfiguration;
import org.jboss.logmanager.config.HandlerConfiguration;
import org.jboss.logmanager.config.LogContextConfiguration;
import org.jboss.logmanager.config.LoggerConfiguration;
import org.jboss.logmanager.config.PojoConfiguration;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
public class LogContextCloseTests {
@Before
public void resetTestObjects() {
TestErrorManager.POJO_OBJECT = null;
TestFilter.POJO_OBJECT = null;
TestFormatter.POJO_OBJECT = null;
TestHandler.ERROR_MANAGER = null;
TestHandler.FILTER = null;
TestHandler.FORMATTER = null;
TestHandler.HANDLERS = null;
TestHandler.IS_CLOSED = false;
TestHandler.POJO_OBJECT = null;
}
@Test
public void testCloseLogContext() throws Exception {
LogContext logContext = LogContext.create();
// Create a test handler to use
final TestHandler handler = new TestHandler();
handler.setErrorManager(new TestErrorManager());
handler.setFilter(new TestFilter());
handler.setFormatter(new TestFormatter());
handler.setLevel(org.jboss.logmanager.Level.TRACE);
final Logger rootLogger = logContext.getLogger("");
rootLogger.setLevel(org.jboss.logmanager.Level.WARN);
final Logger testLogger = logContext.getLogger(LogContextCloseTests.class.getName());
testLogger.setLevel(Level.FINE);
final Logger randomLogger = logContext.getLogger(UUID.randomUUID().toString());
randomLogger.setUseParentFilters(true);
rootLogger.addHandler(handler);
logContext.close();
// Loggers should have no handlers and have been reset
Assert.assertEquals(Level.INFO, rootLogger.getLevel());
final Handler[] handlers = randomLogger.getHandlers();
Assert.assertTrue(handlers == null || handlers.length == 0);
assertEmptyContext(logContext, rootLogger, testLogger, randomLogger);
}
@Test
public void testCloseLogContextConfiguration() throws Exception {
final LogContext logContext = LogContext.create();
final LogContextConfiguration logContextConfiguration = LogContextConfiguration.Factory.create(logContext);
// Add a POJO to ensure it gets removed
final PojoConfiguration pojoConfiguration = logContextConfiguration.addPojoConfiguration(null,
PojoObject.class.getName(), "pojo");
// Create an error manager
final ErrorManagerConfiguration errorManagerConfiguration = logContextConfiguration.addErrorManagerConfiguration(null,
TestErrorManager.class.getName(), "error-manager");
errorManagerConfiguration.setPropertyValueString("pojoObject", pojoConfiguration.getName());
// Create a filter
final FilterConfiguration filterConfiguration = logContextConfiguration.addFilterConfiguration(null,
TestFilter.class.getName(), "filter");
filterConfiguration.setPropertyValueString("pojoObject", pojoConfiguration.getName());
// Create a formatter
final FormatterConfiguration formatterConfiguration = logContextConfiguration.addFormatterConfiguration(null,
TestFormatter.class.getName(), "formatter");
formatterConfiguration.setPropertyValueString("pojoObject", pojoConfiguration.getName());
// Create a handler
final HandlerConfiguration handlerConfiguration = logContextConfiguration.addHandlerConfiguration(null,
TestHandler.class.getName(), "handler");
handlerConfiguration.setPropertyValueString("pojoObject", pojoConfiguration.getName());
handlerConfiguration.setFilter(filterConfiguration.getName());
handlerConfiguration.setErrorManagerName(errorManagerConfiguration.getName());
handlerConfiguration.setFormatterName(formatterConfiguration.getName());
// Create the root-logger configuration
final LoggerConfiguration rootLoggerConfig = logContextConfiguration.addLoggerConfiguration("");
rootLoggerConfig.setFilter(filterConfiguration.getName());
rootLoggerConfig.addHandlerName(handlerConfiguration.getName());
rootLoggerConfig.setLevel("WARN");
final LoggerConfiguration testLoggerConfig = logContextConfiguration.addLoggerConfiguration(LogContextCloseTests.class.getName());
testLoggerConfig.setLevel("DEBUG");
testLoggerConfig.addHandlerName(handlerConfiguration.getName());
final LoggerConfiguration randomLoggerConfig = logContextConfiguration.addLoggerConfiguration(UUID.randomUUID().toString());
randomLoggerConfig.setLevel("ERROR");
randomLoggerConfig.setUseParentHandlers(false);
logContextConfiguration.commit();
// Create the loggers on the log context to test they've been reset, note this is required to be done before
// the context is closed, but after a commit
final Logger rootLogger = logContext.getLogger(rootLoggerConfig.getName());
final Logger testLogger = logContext.getLogger(testLoggerConfig.getName());
final Logger randomLogger = logContext.getLogger(randomLoggerConfig.getName());
logContext.close();
assertEmptyNames("error manager", logContextConfiguration.getErrorManagerNames());
assertEmptyNames("filter", logContextConfiguration.getFilterNames());
assertEmptyNames("formatter", logContextConfiguration.getFormatterNames());
assertEmptyNames("handler", logContextConfiguration.getHandlerNames());
assertEmptyNames("logger", logContextConfiguration.getLoggerNames());
assertEmptyNames("POJO", logContextConfiguration.getPojoNames());
assertEmptyContext(logContext, rootLogger, testLogger, randomLogger);
// The handler is really the only object available for context since it has a close on it
Assert.assertNull("Expected the handler to be reset.", TestHandler.FORMATTER);
// Assert the handler itself has been closed
Assert.assertTrue("The handler was expected to be closed", TestHandler.IS_CLOSED);
}
@Test
public void testCloseWithAttachment() throws Exception {
LogContext logContext = LogContext.create();
final Logger.AttachmentKey<String> key = new Logger.AttachmentKey<>();
final String value = "test value";
Logger rootLogger = logContext.getLogger("");
Assert.assertNull(rootLogger.attach(key, value));
// Close and ensure the context is clean
logContext.close();
Assert.assertNull(rootLogger.getAttachment(key));
assertEmptyContext(logContext, rootLogger);
// Test attachIfAbsent()
logContext = LogContext.create();
rootLogger = logContext.getLogger("");
Assert.assertNull(rootLogger.attachIfAbsent(key, value));
// Close and ensure the context is clean
logContext.close();
Assert.assertNull(rootLogger.getAttachment(key));
assertEmptyContext(logContext, rootLogger);
// Test detach()
logContext = LogContext.create();
rootLogger = logContext.getLogger("");
Assert.assertNull(rootLogger.attach(key, value));
Assert.assertEquals(value, rootLogger.detach(key));
logContext.close();
Assert.assertNull(rootLogger.getAttachment(key));
assertEmptyContext(logContext, rootLogger);
}
private void assertEmptyContext(final LogContext logContext, final Logger... loggers) {
// Inspect the log context and ensure it's "empty"
final LoggerNode rootLogger = logContext.getRootLoggerNode();
final Handler[] handlers = rootLogger.getHandlers();
Assert.assertTrue("Expected the handlers to be removed.", handlers == null || handlers.length == 0);
Assert.assertNull("Expected the filter to be null", rootLogger.getFilter());
Assert.assertEquals("Expected the level to be INFO for logger the root logger", Level.INFO, rootLogger.getLevel());
Assert.assertFalse("Expected the useParentFilters to be false for the root logger", rootLogger.getUseParentFilters());
Assert.assertTrue("Expected the useParentHandlers to be true for the root logger", rootLogger.getUseParentHandlers());
final Collection<LoggerNode> children = rootLogger.getChildren();
if (!children.isEmpty()) {
final StringBuilder msg = new StringBuilder("Expected no children to be remaining on the root logger. Remaining loggers: ");
final Iterator<LoggerNode> iter = children.iterator();
while (iter.hasNext()) {
msg.append('\'').append(iter.next().getFullName()).append('\'');
if (iter.hasNext()) {
msg.append(", ");
}
}
Assert.fail(msg.toString());
}
for (Logger logger : loggers) {
assertLoggerReset(logger);
}
}
private void assertLoggerReset(final Logger logger) {
String loggerName = logger.getName();
final Level expectedLevel;
if ("".equals(loggerName)) {
loggerName = "root";
expectedLevel = Level.INFO;
} else {
expectedLevel = null;
}
final Handler[] handlers = logger.getHandlers();
Assert.assertNull("Expected the filter to be null for logger " + loggerName, logger.getFilter());
Assert.assertTrue("Empty handlers expected for logger " + loggerName, handlers == null || handlers.length == 0);
Assert.assertEquals("Expected the level to be " + expectedLevel + " for logger " + loggerName, expectedLevel, logger.getLevel());
Assert.assertFalse("Expected the useParentFilters to be false for logger " + loggerName, logger.getUseParentFilters());
Assert.assertTrue("Expected the useParentHandlers to be true for logger " + loggerName, logger.getUseParentHandlers());
}
private void assertEmptyNames(final String description, final Collection<String> names) {
Assert.assertTrue(String.format("The configuration should not have any %s names, but found: %s", description, names),
names.isEmpty());
}
@SuppressWarnings("unused")
public static class TestFilter implements Filter {
private static PojoObject POJO_OBJECT;
@Override
public boolean isLoggable(final LogRecord record) {
return true;
}
public void setPojoObject(final PojoObject pojoObject) {
POJO_OBJECT = pojoObject;
}
}
@SuppressWarnings("unused")
public static class TestFormatter extends Formatter {
private static PojoObject POJO_OBJECT;
public void setPojoObject(final PojoObject pojoObject) {
POJO_OBJECT = pojoObject;
}
@Override
public String format(final LogRecord record) {
return ExtLogRecord.wrap(record).getFormattedMessage();
}
}
@SuppressWarnings("unused")
public static class TestErrorManager extends ErrorManager {
private static PojoObject POJO_OBJECT;
public void setPojoObject(final PojoObject pojoObject) {
POJO_OBJECT = pojoObject;
}
}
@SuppressWarnings({"unused", "WeakerAccess"})
public static class TestHandler extends ExtHandler {
private static PojoObject POJO_OBJECT;
private static Handler[] HANDLERS;
private static Formatter FORMATTER;
private static Filter FILTER;
private static ErrorManager ERROR_MANAGER;
private static boolean IS_CLOSED;
public TestHandler() {
IS_CLOSED = false;
}
@Override
public void close() throws SecurityException {
// Null out static values
POJO_OBJECT = null;
FORMATTER = null;
HANDLERS = null;
FORMATTER = null;
FILTER = null;
ERROR_MANAGER = null;
IS_CLOSED = true;
super.close();
}
@Override
public Handler[] setHandlers(final Handler[] newHandlers) throws SecurityException {
HANDLERS = Arrays.copyOf(newHandlers, newHandlers.length);
return super.setHandlers(newHandlers);
}
@Override
public void addHandler(final Handler handler) throws SecurityException {
if (handler == null) {
throw new RuntimeException("Cannot add a null handler");
}
if (HANDLERS == null) {
HANDLERS = new Handler[] {handler};
} else {
final int len = HANDLERS.length + 1;
HANDLERS = Arrays.copyOf(HANDLERS, len);
HANDLERS[len - 1] = handler;
}
super.addHandler(handler);
}
@Override
public void removeHandler(final Handler handler) throws SecurityException {
if (handler == null) {
throw new RuntimeException("Cannot remove a null handler");
}
if (HANDLERS == null) {
throw new RuntimeException("Attempting to remove a handler that does not exist: " + handler);
} else {
if (HANDLERS.length == 1) {
HANDLERS = null;
} else {
boolean success = false;
final Handler[] newHandlers = new Handler[HANDLERS.length - 1];
int newIndex = 0;
for (int i = 0; i < HANDLERS.length; i++) {
final Handler current = HANDLERS[i];
if (!success && i > newHandlers.length) {
break;
}
if (handler != current) {
newHandlers[newIndex++] = current;
} else {
success = true;
}
}
if (!success) {
throw new RuntimeException("Failed to remove handler " + handler + " as it did no appear to exist.");
}
}
}
super.removeHandler(handler);
}
@Override
public void setFormatter(final Formatter newFormatter) throws SecurityException {
FORMATTER = newFormatter;
super.setFormatter(newFormatter);
}
@Override
public void setFilter(final Filter newFilter) throws SecurityException {
FILTER = newFilter;
super.setFilter(newFilter);
}
@Override
public void setErrorManager(final ErrorManager em) {
ERROR_MANAGER = em;
super.setErrorManager(em);
}
@Override
public void setLevel(final Level newLevel) throws SecurityException {
super.setLevel(newLevel);
}
public void setPojoObject(final PojoObject pojoObject) {
POJO_OBJECT = pojoObject;
}
}
@SuppressWarnings("WeakerAccess")
public static class PojoObject {
}
}
......@@ -20,6 +20,7 @@
package org.jboss.logmanager.formatters;
import org.jboss.logmanager.ExtLogRecord;
import org.jboss.logmanager.MDC;
import org.jboss.logmanager.NDC;
import org.junit.Assert;
import org.junit.Test;
......@@ -113,6 +114,28 @@ public class PatternFormatterTests {
Assert.assertEquals("value2.value3", formatter.format(record));
}
@Test
public void mdc() throws Exception {
try {
MDC.put("primaryKey", "primaryValue");
MDC.put("key1", "value1");
MDC.put("key2", "value2");
final ExtLogRecord record = createLogRecord("test");
PatternFormatter formatter = new PatternFormatter("%X{key1}");
Assert.assertEquals("value1", formatter.format(record));
formatter = new PatternFormatter("%X{not.found}");
Assert.assertEquals("", formatter.format(record));
formatter = new PatternFormatter("%X");
String formatted = formatter.format(record);
Assert.assertEquals("{key1=value1, key2=value2, primaryKey=primaryValue}", formatted);
} finally {
MDC.clear();
}
}
@Test
public void threads() throws Exception {
final ExtLogRecord record = createLogRecord("test");
......
......@@ -100,7 +100,7 @@ public class PeriodicRotatingFileHandlerTests extends AbstractHandlerTest {
targetClass = "java.nio.file.Files",
targetMethod = "move",
targetLocation = "AT ENTRY",
condition = "$2.getFileName().toString().matches(\"periodic-rotating-file-handler\\.log\\.\\d+\")",
condition = "$2.getFileName().toString().matches(\"periodic-rotating-file-handler\\\\.log\\\\.\\\\d+\")",
action = "throw new IOException(\"Fail on purpose\")")
public void testFailedRotate() throws Exception {
final Calendar cal = Calendar.getInstance();
......