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 }