1 /* 2 Copyright (C) 2013 Red Hat 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 19 package net.sourceforge.jnlp.util.lockingfile; 20 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertTrue; 23 import org.junit.Before; 24 import java.io.BufferedWriter; 25 import java.io.File; 26 import java.io.IOException; 27 import java.util.ArrayList; 28 import java.util.List; 29 import org.junit.Assert; 30 import org.junit.Test; 31 32 public class LockingReaderWriterTest { 33 34 private static File storagefile; 35 newInstance()36 private static TestStringReaderWriter newInstance() { 37 return new TestStringReaderWriter(storagefile); 38 } 39 40 @Before setUp()41 public void setUp() throws IOException { 42 storagefile = File.createTempFile("foo", "bar"); 43 } 44 45 @Test testSimpleActions()46 public void testSimpleActions() throws IOException { 47 TestStringReaderWriter storage = newInstance(); 48 49 storage.add("teststring"); 50 assertTrue(storage.contains("teststring")); 51 storage.remove("teststring"); 52 assertFalse(storage.contains("teststring")); 53 } 54 55 @Test testInterleavedActions()56 public void testInterleavedActions() throws IOException { 57 TestStringReaderWriter storage1 = newInstance(); 58 TestStringReaderWriter storage2 = newInstance(); 59 60 storage1.add("teststring"); 61 assertTrue(storage2.contains("teststring")); 62 storage2.remove("teststring"); 63 assertFalse(storage1.contains("teststring")); 64 } 65 66 static class TestThread extends Thread { 67 String testString; 68 int iterations; 69 Throwable error = null; 70 TestThread(String testString, int iterations)71 TestThread(String testString, int iterations) { 72 this.testString = testString; 73 this.iterations = iterations; 74 } 75 76 @Override run()77 public void run() { 78 try { 79 TestStringReaderWriter storage = newInstance(); 80 for (int i = 0; i < iterations; i++) { 81 assertTrue(storage.contains(this.testString)); 82 storage.add(this.testString); 83 storage.remove(this.testString); 84 assertTrue(storage.contains(this.testString)); 85 } 86 } catch (Throwable error) { 87 error.printStackTrace(); 88 this.error = error; 89 } 90 } 91 } 92 concurrentReadWrites(int threadAmount, int iterations, String testString)93 private void concurrentReadWrites(int threadAmount, int iterations, 94 String testString) throws InterruptedException { 95 TestStringReaderWriter storage = newInstance(); 96 97 storage.add(testString); 98 99 List<TestThread> testThreads = new ArrayList<TestThread>(); 100 101 for (int i = 0; i < threadAmount; i++) { 102 TestThread thread = new TestThread(testString, iterations); 103 testThreads.add(thread); 104 thread.start(); 105 } 106 107 for (int i = 0; i < threadAmount; i++) { 108 testThreads.get(i).join(); 109 } 110 111 assertTrue(storage.contains(testString)); 112 storage.remove(testString); 113 114 // So long as number adds == number writes, we should be left with 115 // nothing at end. 116 assertFalse(storage.contains(testString)); 117 } 118 119 // Long testing string, the contents are not important makeLongTestString()120 private String makeLongTestString() { 121 StringBuilder sb = new StringBuilder(); 122 for (int i = 0; i < 1000; i++) { 123 sb.append(Integer.toString(i)); 124 } 125 return sb.toString(); 126 } 127 128 @Test testManyReadWrite()129 public void testManyReadWrite() throws Exception { 130 int oneThread = 1; 131 String shortString = "teststring"; 132 133 // This was causing 'too many open files' because FileUtils#getFileLock 134 // leaks file descriptors. No longer used. 135 concurrentReadWrites(oneThread, 500 /* iterations */, shortString); 136 } 137 138 @Test testManyThreads()139 public void testManyThreads() throws Exception { 140 int threadAmount = 25; 141 String shortString = "teststring"; 142 String longString = makeLongTestString(); 143 144 concurrentReadWrites(threadAmount, 10 /* per-thread iterations */, 145 shortString); 146 concurrentReadWrites(threadAmount, 2 /* per-thread iterations */, 147 longString); 148 } 149 150 /** 151 * Concrete implementation to aid in testing LockingReaderWriter 152 */ 153 public static class TestStringReaderWriter extends LockingReaderWriter { 154 155 private List<String> cachedContents = new ArrayList<String>(); 156 TestStringReaderWriter(File file)157 public TestStringReaderWriter(File file) { 158 super(file); 159 } 160 161 @Override writeContent(BufferedWriter writer)162 public void writeContent(BufferedWriter writer) throws IOException { 163 for (String string : cachedContents) { 164 writer.write(string); 165 writer.newLine(); 166 } 167 } 168 169 @Override readLine(String line)170 protected void readLine(String line) { 171 this.cachedContents.add(line); 172 } 173 174 @Override readContents()175 protected void readContents() throws IOException { 176 cachedContents.clear(); 177 super.readContents(); 178 } 179 180 /* 181 * Atomic container abstraction methods. 182 */ add(final String line)183 synchronized public void add(final String line) { 184 doLocked(new Runnable() { 185 186 public void run() { 187 try { 188 readContents(); 189 cachedContents.add(line); 190 writeContents(); 191 } catch (IOException ex) { 192 throw new StorageIoException(ex); 193 } 194 } 195 }); 196 } 197 contains(final String line)198 synchronized public boolean contains(final String line) { 199 final boolean[] doesContain = { false }; 200 doLocked(new Runnable() { 201 202 public void run() { 203 try { 204 readContents(); 205 doesContain[0] = cachedContents.contains(line); 206 } catch (IOException e) { 207 throw new StorageIoException(e); 208 } 209 } 210 }); 211 return doesContain[0]; 212 } 213 remove(final String line)214 synchronized public boolean remove(final String line) { 215 final boolean[] didRemove = { false }; 216 217 doLocked(new Runnable() { 218 public void run() { 219 try { 220 readContents(); 221 didRemove[0] = cachedContents.remove(line); 222 writeContents(); 223 } catch (IOException e) { 224 throw new StorageIoException(e); 225 } 226 } 227 }); 228 229 return didRemove[0]; 230 } 231 } 232 }