1 /**
2 * Logback: the reliable, generic, fast and flexible logging framework.
3 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4 *
5 * This program and the accompanying materials are dual-licensed under
6 * either the terms of the Eclipse Public License v1.0 as published by
7 * the Eclipse Foundation
8 *
9 * or (per the licensee's choosing)
10 *
11 * under the terms of the GNU Lesser General Public License version 2.1
12 * as published by the Free Software Foundation.
13 */
14 package ch.qos.logback.core.spi;
15
16 import ch.qos.logback.core.Appender;
17 import org.junit.jupiter.api.Disabled;
18 import org.junit.jupiter.api.Test;
19 import org.junit.jupiter.api.Timeout;
20
21 import java.util.concurrent.TimeUnit;
22
23 import static org.mockito.Mockito.mock;
24 import static org.mockito.Mockito.when;
25
26 /**
27 * This test shows the general problem I described in LOGBACK-102.
28 *
29 * In the two test cases below, an appender that throws an RuntimeException
30 * while getName is called - but this is just an example to show the general
31 * problem.
32 *
33 * The tests below fail without fixing LBCORE-67 and pass when Joern Huxhorn's
34 * patch is applied.
35 *
36 * Additionally, the following, probably more realistic, situations could
37 * happen:
38 *
39 * -addAppender: appenderList.add() could throw OutOfMemoryError. This could
40 * only be shown by using an appenderList mock but appenderList does not (and
41 * should not) have a setter. This would leave the write lock locked.
42 *
43 * -iteratorForAppenders: new ArrayList() could throw an OutOfMemoryError,
44 * leaving the read lock locked.
45 *
46 * I can't imagine a bad situation in isAttached, detachAppender(Appender) or
47 * detachAppender(String) but I'd change the code anyway for consistency. I'm
48 * also pretty sure that something stupid can happen at any time so it's best to
49 * just stick to conventions.
50 *
51 * @author Joern Huxhorn
52 */
53 @Disabled
54 public class AppenderAttachableImplLockTest {
55
56 private AppenderAttachableImpl<Integer> aai = new AppenderAttachableImpl<Integer>();
57
58 @SuppressWarnings("unchecked")
59 @Test
60 @Timeout(value=1, unit = TimeUnit.SECONDS)
61 public void getAppenderBoom() {
62 Appender<Integer> mockAppender1 = mock(Appender.class);
63
64 when(mockAppender1.getName()).thenThrow(new RuntimeException("oops"));
65 aai.addAppender(mockAppender1);
66 try {
67 // appender.getName called as a result of next statement
68 aai.getAppender("foo");
69 } catch (RuntimeException e) {
70 // this leaves the read lock locked.
71 }
72
73 Appender<Integer> mockAppender2 = mock(Appender.class);
74 // the next call used to freeze with the earlier ReadWriteLock lock
75 // implementation
76 aai.addAppender(mockAppender2);
77 }
78
79 @SuppressWarnings("unchecked")
80 @Test
81 @Timeout(value=1, unit = TimeUnit.SECONDS)
82 public void detachAppenderBoom() throws InterruptedException {
83 Appender<Integer> mockAppender = mock(Appender.class);
84 when(mockAppender.getName()).thenThrow(new RuntimeException("oops"));
85 mockAppender.doAppend(17);
86
87 aai.addAppender(mockAppender);
88 Thread t = new Thread(new Runnable() {
89
90 public void run() {
91 try {
92 // appender.getName called as a result of next statement
93 aai.detachAppender("foo");
94 } catch (RuntimeException e) {
95 System.out.println("Caught " + e.toString());
96 // this leaves the write lock locked.
97 }
98 }
99 });
100 t.start();
101 t.join();
102
103 // the next call used to freeze with the earlier ReadWriteLock lock
104 // implementation
105 aai.appendLoopOnAppenders(17);
106 }
107 }