Revert "Disallow setting logger factory twice"

This reverts commit 3c92f2b64a which needs more thoughts and so will go into the next release.
This commit is contained in:
Norman Maurer 2016-12-21 15:14:53 +01:00
parent 28c39a3183
commit 31da0ddbac
2 changed files with 55 additions and 265 deletions

View File

@ -16,64 +16,24 @@
package io.netty.util.internal.logging;
import java.util.concurrent.atomic.AtomicReference;
/**
* Creates {@link InternalLogger}s. This factory allows you to choose what logging framework Netty should use. The
* default factory is {@link Slf4JLoggerFactory}. If SLF4J is not available, {@link Log4JLoggerFactory} is used. If
* Log4J is not available, {@link JdkLoggerFactory} is used. You can change it to your preferred logging framework
* before other Netty classes are loaded via {@link #setDefaultFactory(InternalLoggerFactory)}. If you want to change
* the logger factory, {@link #setDefaultFactory(InternalLoggerFactory)} must be invoked before any other Netty classes
* are loaded. Note that {@link #setDefaultFactory(InternalLoggerFactory)}} can not be invoked more than once.
* Creates an {@link InternalLogger} or changes the default factory
* implementation. This factory allows you to choose what logging framework
* Netty should use. The default factory is {@link Slf4JLoggerFactory}. If SLF4J
* is not available, {@link Log4JLoggerFactory} is used. If Log4J is not available,
* {@link JdkLoggerFactory} is used. You can change it to your preferred
* logging framework before other Netty classes are loaded:
* <pre>
* {@link InternalLoggerFactory}.setDefaultFactory({@link Log4JLoggerFactory}.INSTANCE);
* </pre>
* Please note that the new default factory is effective only for the classes
* which were loaded after the default factory is changed. Therefore,
* {@link #setDefaultFactory(InternalLoggerFactory)} should be called as early
* as possible and shouldn't be called more than once.
*/
public abstract class InternalLoggerFactory {
private static final InternalLoggerFactoryHolder HOLDER = new InternalLoggerFactoryHolder();
/**
* This class holds a reference to the {@link InternalLoggerFactory}. The raison d'être for this class is primarily
* to aid in testing.
*/
static final class InternalLoggerFactoryHolder {
private final AtomicReference<InternalLoggerFactory> reference;
InternalLoggerFactoryHolder() {
this(null);
}
InternalLoggerFactoryHolder(final InternalLoggerFactory holder) {
this.reference = new AtomicReference<InternalLoggerFactory>(holder);
}
InternalLoggerFactory getFactory() {
if (reference.get() == null) {
reference.compareAndSet(null, newDefaultFactory(InternalLoggerFactory.class.getName()));
}
return reference.get();
}
void setFactory(final InternalLoggerFactory factory) {
if (factory == null) {
throw new NullPointerException("factory");
}
if (!reference.compareAndSet(null, factory)) {
throw new IllegalStateException(
"factory is already set to [" + reference.get() + "], rejecting [" + factory + "]");
}
}
InternalLogger getInstance(final Class<?> clazz) {
return getInstance(clazz.getName());
}
InternalLogger getInstance(final String name) {
return newInstance(name);
}
InternalLogger newInstance(String name) {
return getFactory().newInstance(name);
}
}
private static volatile InternalLoggerFactory defaultFactory;
@SuppressWarnings("UnusedCatchParameter")
private static InternalLoggerFactory newDefaultFactory(String name) {
@ -94,35 +54,38 @@ public abstract class InternalLoggerFactory {
}
/**
* Get the default factory that was either initialized automatically based on logging implementations on the
* classpath, or set explicitly via {@link #setDefaultFactory(InternalLoggerFactory)}.
* Returns the default factory. The initial default factory is
* {@link JdkLoggerFactory}.
*/
public static InternalLoggerFactory getDefaultFactory() {
return HOLDER.getFactory();
if (defaultFactory == null) {
defaultFactory = newDefaultFactory(InternalLoggerFactory.class.getName());
}
return defaultFactory;
}
/**
* Set the default factory. This method must be invoked before the default factory is initialized via
* {@link #getDefaultFactory()}, and can not be invoked multiple times.
*
* @param defaultFactory a non-null implementation of {@link InternalLoggerFactory}
* Changes the default factory.
*/
public static void setDefaultFactory(InternalLoggerFactory defaultFactory) {
HOLDER.setFactory(defaultFactory);
if (defaultFactory == null) {
throw new NullPointerException("defaultFactory");
}
InternalLoggerFactory.defaultFactory = defaultFactory;
}
/**
* Creates a new logger instance with the name of the specified class.
*/
public static InternalLogger getInstance(Class<?> clazz) {
return HOLDER.getInstance(clazz);
return getInstance(clazz.getName());
}
/**
* Creates a new logger instance with the specified name.
*/
public static InternalLogger getInstance(String name) {
return HOLDER.getInstance(name);
return getDefaultFactory().newInstance(name);
}
/**

View File

@ -13,63 +13,49 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.internal.logging;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
public class InternalLoggerFactoryTest {
private static final Exception e = new Exception();
private InternalLoggerFactory.InternalLoggerFactoryHolder holder;
private InternalLoggerFactory oldLoggerFactory;
private InternalLogger mock;
@Before
public void init() {
final InternalLoggerFactory mockFactory = createMock(InternalLoggerFactory.class);
oldLoggerFactory = InternalLoggerFactory.getDefaultFactory();
InternalLoggerFactory mockFactory = createMock(InternalLoggerFactory.class);
mock = createStrictMock(InternalLogger.class);
expect(mockFactory.newInstance("mock")).andReturn(mock).anyTimes();
replay(mockFactory);
holder = new InternalLoggerFactory.InternalLoggerFactoryHolder(mockFactory);
InternalLoggerFactory.setDefaultFactory(mockFactory);
}
@After
public void destroy() {
reset(mock);
InternalLoggerFactory.setDefaultFactory(oldLoggerFactory);
}
@Test(expected = NullPointerException.class)
public void shouldNotAllowNullDefaultFactory() {
holder.setFactory(null);
InternalLoggerFactory.setDefaultFactory(null);
}
@Test
public void shouldGetInstance() {
final String helloWorld = "Hello, world!";
InternalLoggerFactory.setDefaultFactory(oldLoggerFactory);
final InternalLogger one = InternalLoggerFactory.getInstance("helloWorld");
final InternalLogger two = InternalLoggerFactory.getInstance(helloWorld.getClass());
String helloWorld = "Hello, world!";
InternalLogger one = InternalLoggerFactory.getInstance("helloWorld");
InternalLogger two = InternalLoggerFactory.getInstance(helloWorld.getClass());
assertNotNull(one);
assertNotNull(two);
@ -81,7 +67,7 @@ public class InternalLoggerFactoryTest {
expect(mock.isTraceEnabled()).andReturn(true);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
assertTrue(logger.isTraceEnabled());
verify(mock);
}
@ -91,7 +77,7 @@ public class InternalLoggerFactoryTest {
expect(mock.isDebugEnabled()).andReturn(true);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
assertTrue(logger.isDebugEnabled());
verify(mock);
}
@ -101,7 +87,7 @@ public class InternalLoggerFactoryTest {
expect(mock.isInfoEnabled()).andReturn(true);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
assertTrue(logger.isInfoEnabled());
verify(mock);
}
@ -111,7 +97,7 @@ public class InternalLoggerFactoryTest {
expect(mock.isWarnEnabled()).andReturn(true);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
assertTrue(logger.isWarnEnabled());
verify(mock);
}
@ -121,7 +107,7 @@ public class InternalLoggerFactoryTest {
expect(mock.isErrorEnabled()).andReturn(true);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
assertTrue(logger.isErrorEnabled());
verify(mock);
}
@ -131,7 +117,7 @@ public class InternalLoggerFactoryTest {
mock.trace("a");
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.trace("a");
verify(mock);
}
@ -141,7 +127,7 @@ public class InternalLoggerFactoryTest {
mock.trace("a", e);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.trace("a", e);
verify(mock);
}
@ -151,7 +137,7 @@ public class InternalLoggerFactoryTest {
mock.debug("a");
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.debug("a");
verify(mock);
}
@ -161,7 +147,7 @@ public class InternalLoggerFactoryTest {
mock.debug("a", e);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.debug("a", e);
verify(mock);
}
@ -171,7 +157,7 @@ public class InternalLoggerFactoryTest {
mock.info("a");
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.info("a");
verify(mock);
}
@ -181,7 +167,7 @@ public class InternalLoggerFactoryTest {
mock.info("a", e);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.info("a", e);
verify(mock);
}
@ -191,7 +177,7 @@ public class InternalLoggerFactoryTest {
mock.warn("a");
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.warn("a");
verify(mock);
}
@ -201,7 +187,7 @@ public class InternalLoggerFactoryTest {
mock.warn("a", e);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.warn("a", e);
verify(mock);
}
@ -211,7 +197,7 @@ public class InternalLoggerFactoryTest {
mock.error("a");
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.error("a");
verify(mock);
}
@ -221,167 +207,8 @@ public class InternalLoggerFactoryTest {
mock.error("a", e);
replay(mock);
final InternalLogger logger = holder.getInstance("mock");
InternalLogger logger = InternalLoggerFactory.getInstance("mock");
logger.error("a", e);
verify(mock);
}
@Test
public void shouldNotAllowToSetFactoryTwice() {
try {
holder.setFactory(createMock(InternalLoggerFactory.class));
fail("should have thrown IllegalStateException");
} catch (final IllegalStateException e) {
assertThat(e.getMessage(), containsString("factory is already set"));
}
try {
final InternalLoggerFactory.InternalLoggerFactoryHolder implicit =
new InternalLoggerFactory.InternalLoggerFactoryHolder();
implicit.getFactory(); // force initialization
implicit.setFactory(createMock(InternalLoggerFactory.class));
fail("should have thrown IllegalStateException");
} catch (final IllegalStateException e) {
assertThat(e.getMessage(), containsString("factory is already set"));
}
}
@Test
public void raceGetAndGet() throws BrokenBarrierException, InterruptedException {
final CyclicBarrier barrier = new CyclicBarrier(3);
final InternalLoggerFactory.InternalLoggerFactoryHolder holder =
new InternalLoggerFactory.InternalLoggerFactoryHolder();
final AtomicReference<InternalLoggerFactory> firstReference = new AtomicReference<InternalLoggerFactory>();
final AtomicReference<InternalLoggerFactory> secondReference = new AtomicReference<InternalLoggerFactory>();
final Thread firstGet = getThread(firstReference, holder, barrier);
final Thread secondGet = getThread(secondReference, holder, barrier);
firstGet.start();
secondGet.start();
// start the two get threads
barrier.await();
// wait for the two get threads to complete
barrier.await();
if (holder.getFactory() == firstReference.get()) {
assertSame(holder.getFactory(), secondReference.get());
} else if (holder.getFactory() == secondReference.get()) {
assertSame(holder.getFactory(), firstReference.get());
} else {
fail("holder should have been set by one of the get threads");
}
}
@Test
public void raceGetAndSet() throws BrokenBarrierException, InterruptedException {
final CyclicBarrier barrier = new CyclicBarrier(3);
final InternalLoggerFactory.InternalLoggerFactoryHolder holder =
new InternalLoggerFactory.InternalLoggerFactoryHolder();
final InternalLoggerFactory internalLoggerFactory = createMock(InternalLoggerFactory.class);
final AtomicReference<InternalLoggerFactory> reference = new AtomicReference<InternalLoggerFactory>();
final Thread get = getThread(reference, holder, barrier);
final AtomicBoolean setSuccess = new AtomicBoolean();
final Thread set = setThread(internalLoggerFactory, holder, setSuccess, barrier);
get.start();
set.start();
// start the get and set threads
barrier.await();
// wait for the get and set threads to complete
barrier.await();
if (setSuccess.get()) {
assertSame(internalLoggerFactory, reference.get());
assertSame(internalLoggerFactory, holder.getFactory());
} else {
assertNotSame(internalLoggerFactory, reference.get());
assertNotSame(internalLoggerFactory, holder.getFactory());
assertSame(holder.getFactory(), reference.get());
}
}
@Test
public void raceSetAndSet() throws BrokenBarrierException, InterruptedException {
final CyclicBarrier barrier = new CyclicBarrier(3);
final InternalLoggerFactory.InternalLoggerFactoryHolder holder =
new InternalLoggerFactory.InternalLoggerFactoryHolder();
final InternalLoggerFactory first = createMock(InternalLoggerFactory.class);
final InternalLoggerFactory second = createMock(InternalLoggerFactory.class);
final AtomicBoolean firstSetSuccess = new AtomicBoolean();
final Thread firstSet = setThread(first, holder, firstSetSuccess, barrier);
final AtomicBoolean secondSetSuccess = new AtomicBoolean();
final Thread secondSet = setThread(second, holder, secondSetSuccess, barrier);
firstSet.start();
secondSet.start();
// start the two set threads
barrier.await();
// wait for the two set threads to complete
barrier.await();
assertTrue(firstSetSuccess.get() || secondSetSuccess.get());
if (firstSetSuccess.get()) {
assertFalse(secondSetSuccess.get());
assertSame(first, holder.getFactory());
} else {
assertFalse(firstSetSuccess.get());
assertSame(second, holder.getFactory());
}
}
private static Thread getThread(
final AtomicReference<InternalLoggerFactory> reference,
final InternalLoggerFactory.InternalLoggerFactoryHolder holder,
final CyclicBarrier barrier) {
return new Thread(new Runnable() {
@Override
public void run() {
awaitUnchecked(barrier);
reference.set(holder.getFactory());
awaitUnchecked(barrier);
}
});
}
private static Thread setThread(
final InternalLoggerFactory internalLoggerFactory,
final InternalLoggerFactory.InternalLoggerFactoryHolder holder,
final AtomicBoolean setSuccess,
final CyclicBarrier barrier) {
return new Thread(new Runnable() {
@Override
public void run() {
awaitUnchecked(barrier);
boolean success = true;
try {
holder.setFactory(internalLoggerFactory);
} catch (final IllegalStateException e) {
success = false;
assertThat(e.getMessage(), containsString("factory is already set"));
} finally {
setSuccess.set(success);
awaitUnchecked(barrier);
}
}
});
}
private static void awaitUnchecked(final CyclicBarrier barrier) {
try {
barrier.await();
} catch (final InterruptedException exception) {
throw new IllegalStateException(exception);
} catch (final BrokenBarrierException exception) {
throw new IllegalStateException(exception);
}
}
}