1 package org.eclipse.aether.connector.basic;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import static org.junit.Assert.*;
23 import static org.junit.Assume.*;
24
25 import java.io.Closeable;
26 import java.io.File;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.RandomAccessFile;
30 import java.nio.channels.FileLock;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Locale;
34 import java.util.concurrent.CountDownLatch;
35
36 import org.eclipse.aether.internal.test.util.TestFileUtils;
37 import org.eclipse.aether.internal.test.util.TestLoggerFactory;
38 import org.junit.After;
39 import org.junit.Before;
40 import org.junit.Test;
41
42 public class PartialFileTest
43 {
44
45 private static class StubRemoteAccessChecker
46 implements PartialFile.RemoteAccessChecker
47 {
48
49 Exception exception;
50
51 int invocations;
52
53 public void checkRemoteAccess()
54 throws Exception
55 {
56 invocations++;
57 if ( exception != null )
58 {
59 throw exception;
60 }
61 }
62
63 }
64
65 private static class ConcurrentWriter
66 extends Thread
67 {
68
69 private final File dstFile;
70
71 private final File partFile;
72
73 private final File lockFile;
74
75 private final CountDownLatch locked;
76
77 private final int sleep;
78
79 volatile int length;
80
81 Exception error;
82
83 public ConcurrentWriter( File dstFile, int sleep, int length )
84 throws InterruptedException
85 {
86 super( "ConcurrentWriter-" + dstFile.getAbsolutePath() );
87 this.dstFile = dstFile;
88 partFile = new File( dstFile.getPath() + PartialFile.EXT_PART );
89 lockFile = new File( partFile.getPath() + PartialFile.EXT_LOCK );
90 this.sleep = sleep;
91 this.length = length;
92 locked = new CountDownLatch( 1 );
93 start();
94 locked.await();
95 }
96
97 @Override
98 public void run()
99 {
100 try
101 {
102 RandomAccessFile raf = new RandomAccessFile( lockFile, "rw" );
103 try
104 {
105 FileLock lock = raf.getChannel().lock( 0, 1, false );
106 locked.countDown();
107 FileOutputStream fos = new FileOutputStream( partFile );
108 try
109 {
110 for ( int i = 0, n = Math.abs( length ); i < n; i++ )
111 {
112 for ( long start = System.currentTimeMillis(); System.currentTimeMillis() - start < sleep; )
113 {
114 Thread.sleep( 10 );
115 }
116 fos.write( 65 );
117 fos.flush();
118 System.out.println( " " + System.currentTimeMillis() + " Wrote byte " + ( i + 1 ) + "/"
119 + n );
120 }
121 if ( length >= 0 && !dstFile.setLastModified( System.currentTimeMillis() ) )
122 {
123 throw new IOException( "Could not update destination file" );
124 }
125 }
126 finally
127 {
128 fos.close();
129 }
130 lock.release();
131 }
132 finally
133 {
134 raf.close();
135 lockFile.delete();
136 }
137 }
138 catch ( Exception e )
139 {
140 error = e;
141 }
142 }
143
144 }
145
146 private static final boolean PROPER_LOCK_SUPPORT;
147
148 static
149 {
150 String javaVersion = System.getProperty( "java.version" ).trim();
151 boolean notJava5 = !javaVersion.startsWith( "1.5." );
152 String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
153 boolean windows = osName.contains( "windows" );
154 PROPER_LOCK_SUPPORT = notJava5 || windows;
155 }
156
157 private StubRemoteAccessChecker remoteAccessChecker;
158
159 private File dstFile;
160
161 private File partFile;
162
163 private File lockFile;
164
165 private List<Closeable> closeables;
166
167 private PartialFile newPartialFile( long resumeThreshold, int requestTimeout )
168 throws Exception
169 {
170 PartialFile.Factory factory =
171 new PartialFile.Factory( resumeThreshold >= 0, resumeThreshold, requestTimeout,
172 new TestLoggerFactory().getLogger( "" ) );
173 PartialFile partFile = factory.newInstance( dstFile, remoteAccessChecker );
174 if ( partFile != null )
175 {
176 closeables.add( partFile );
177 }
178 return partFile;
179 }
180
181 @Before
182 public void init()
183 throws Exception
184 {
185 closeables = new ArrayList<Closeable>();
186 remoteAccessChecker = new StubRemoteAccessChecker();
187 dstFile = TestFileUtils.createTempFile( "Hello World!" );
188 partFile = new File( dstFile.getPath() + PartialFile.EXT_PART );
189 lockFile = new File( partFile.getPath() + PartialFile.EXT_LOCK );
190 }
191
192 @After
193 public void exit()
194 {
195 for ( Closeable closeable : closeables )
196 {
197 try
198 {
199 closeable.close();
200 }
201 catch ( Exception e )
202 {
203 e.printStackTrace();
204 }
205 }
206 }
207
208 @Test
209 public void testCloseNonResumableFile()
210 throws Exception
211 {
212 PartialFile partialFile = newPartialFile( -1, 100 );
213 assertNotNull( partialFile );
214 assertNotNull( partialFile.getFile() );
215 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
216 partialFile.close();
217 assertFalse( partialFile.getFile().getAbsolutePath(), partialFile.getFile().exists() );
218 }
219
220 @Test
221 public void testCloseResumableFile()
222 throws Exception
223 {
224 PartialFile partialFile = newPartialFile( 0, 100 );
225 assertNotNull( partialFile );
226 assertNotNull( partialFile.getFile() );
227 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
228 assertEquals( partFile, partialFile.getFile() );
229 assertTrue( lockFile.getAbsolutePath(), lockFile.isFile() );
230 partialFile.close();
231 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
232 assertFalse( lockFile.getAbsolutePath(), lockFile.exists() );
233 }
234
235 @Test
236 public void testResumableFileCreationError()
237 throws Exception
238 {
239 assertTrue( partFile.getAbsolutePath(), partFile.mkdirs() );
240 PartialFile partialFile = newPartialFile( 0, 100 );
241 assertNotNull( partialFile );
242 assertFalse( partialFile.isResume() );
243 assertFalse( lockFile.getAbsolutePath(), lockFile.exists() );
244 }
245
246 @Test
247 public void testResumeThreshold()
248 throws Exception
249 {
250 PartialFile partialFile = newPartialFile( 0, 100 );
251 assertNotNull( partialFile );
252 assertTrue( partialFile.isResume() );
253 partialFile.close();
254 partialFile = newPartialFile( 1, 100 );
255 assertNotNull( partialFile );
256 assertFalse( partialFile.isResume() );
257 partialFile.close();
258 }
259
260 @Test( timeout = 10000 )
261 public void testResumeConcurrently_RequestTimeout()
262 throws Exception
263 {
264 assumeTrue( PROPER_LOCK_SUPPORT );
265 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 5 * 1000, 1 );
266 try
267 {
268 newPartialFile( 0, 1000 );
269 fail( "expected exception" );
270 }
271 catch ( Exception e )
272 {
273 assertTrue( e.getMessage().contains( "Timeout" ) );
274 }
275 writer.interrupt();
276 writer.join();
277 }
278
279 @Test( timeout = 10000 )
280 public void testResumeConcurrently_AwaitCompletion_ConcurrentWriterSucceeds()
281 throws Exception
282 {
283 assumeTrue( PROPER_LOCK_SUPPORT );
284 assertTrue( dstFile.setLastModified( System.currentTimeMillis() - 60 * 1000 ) );
285 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 100, 10 );
286 assertNull( newPartialFile( 0, 500 ) );
287 writer.join();
288 assertNull( writer.error );
289 assertEquals( 1, remoteAccessChecker.invocations );
290 }
291
292 @Test( timeout = 10000 )
293 public void testResumeConcurrently_AwaitCompletion_ConcurrentWriterFails()
294 throws Exception
295 {
296 assumeTrue( PROPER_LOCK_SUPPORT );
297 assertTrue( dstFile.setLastModified( System.currentTimeMillis() - 60 * 1000 ) );
298 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 100, -10 );
299 PartialFile partialFile = newPartialFile( 0, 500 );
300 assertNotNull( partialFile );
301 assertTrue( partialFile.isResume() );
302 writer.join();
303 assertNull( writer.error );
304 assertEquals( 1, remoteAccessChecker.invocations );
305 }
306
307 @Test( timeout = 10000 )
308 public void testResumeConcurrently_CheckRemoteAccess()
309 throws Exception
310 {
311 assumeTrue( PROPER_LOCK_SUPPORT );
312 remoteAccessChecker.exception = new IOException( "missing" );
313 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 1000, 1 );
314 try
315 {
316 newPartialFile( 0, 1000 );
317 fail( "expected exception" );
318 }
319 catch ( Exception e )
320 {
321 assertSame( remoteAccessChecker.exception, e );
322 }
323 writer.interrupt();
324 writer.join();
325 }
326
327 }