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