001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.spi; 015 016import ch.qos.logback.core.Appender; 017import org.junit.Test; 018import static org.mockito.Mockito.mock; 019import static org.mockito.Mockito.when; 020 021/** 022 * This test shows the general problem I described in LBCORE-67. 023 * 024 * In the two test cases below, an appender that throws an OutOfMemoryError 025 * while getName is called - but this is just an example to show the general 026 * problem. 027 * 028 * The tests below fail without fixing LBCORE-67 and pass when Joern Huxhorn's 029 * patch is applied. 030 * 031 * Additionally, the following, probably more realistic, situations could 032 * happen: 033 * 034 * -addAppender: appenderList.add() could throw OutOfMemoryError. This could 035 * only be shown by using an appenderList mock but appenderList does not (and 036 * should not) have a setter. This would leave the write lock locked. 037 * 038 * -iteratorForAppenders: new ArrayList() could throw an OutOfMemoryError, 039 * leaving the read lock locked. 040 * 041 * I can't imagine a bad situation in isAttached, detachAppender(Appender) or 042 * detachAppender(String) but I'd change the code anyway for consistency. I'm 043 * also pretty sure that something stupid can happen at any time so it's best to 044 * just stick to conventions. 045 * 046 * @author Joern Huxhorn 047 */ 048public class AppenderAttachableImplLockTest { 049 050 private AppenderAttachableImpl<Integer> aai = new AppenderAttachableImpl<Integer>(); 051 052 @SuppressWarnings("unchecked") 053 @Test(timeout = 5000) 054 public void getAppenderBoom() { 055 Appender<Integer> mockAppender1 = mock(Appender.class); 056 057 when(mockAppender1.getName()).thenThrow(new OutOfMemoryError("oops")); 058 aai.addAppender(mockAppender1); 059 try { 060 // appender.getName called as a result of next statement 061 aai.getAppender("foo"); 062 } catch (OutOfMemoryError e) { 063 // this leaves the read lock locked. 064 } 065 066 Appender<Integer> mockAppender2 = mock(Appender.class); 067 // the next call used to freeze with the earlier ReadWriteLock lock 068 // implementation 069 aai.addAppender(mockAppender2); 070 } 071 072 @SuppressWarnings("unchecked") 073 @Test(timeout = 5000) 074 public void detachAppenderBoom() throws InterruptedException { 075 Appender<Integer> mockAppender = mock(Appender.class); 076 when(mockAppender.getName()).thenThrow(new OutOfMemoryError("oops")); 077 mockAppender.doAppend(17); 078 079 aai.addAppender(mockAppender); 080 Thread t = new Thread(new Runnable() { 081 082 public void run() { 083 try { 084 // appender.getName called as a result of next statement 085 aai.detachAppender("foo"); 086 } catch (OutOfMemoryError e) { 087 // this leaves the write lock locked. 088 } 089 } 090 }); 091 t.start(); 092 t.join(); 093 094 // the next call used to freeze with the earlier ReadWriteLock lock 095 // implementation 096 aai.appendLoopOnAppenders(17); 097 } 098}