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