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.OutputStream;
30 import java.io.RandomAccessFile;
31 import java.nio.channels.FileLock;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.concurrent.CountDownLatch;
36
37 import org.eclipse.aether.internal.test.util.TestFileUtils;
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 RandomAccessFile raf = null;
101 FileLock lock = null;
102 OutputStream out = null;
103 try
104 {
105 raf = new RandomAccessFile( lockFile, "rw" );
106 lock = raf.getChannel().lock( 0, 1, false );
107 locked.countDown();
108 out = new FileOutputStream( partFile );
109 for ( int i = 0, n = Math.abs( length ); i < n; i++ )
110 {
111 for ( long start = System.currentTimeMillis(); System.currentTimeMillis() - start < sleep; )
112 {
113 Thread.sleep( 10 );
114 }
115 out.write( 65 );
116 out.flush();
117 System.out.println( " " + System.currentTimeMillis() + " Wrote byte " + ( i + 1 ) + "/"
118 + n );
119 }
120 if ( length >= 0 && !dstFile.setLastModified( System.currentTimeMillis() ) )
121 {
122 throw new IOException( "Could not update destination file" );
123 }
124
125 out.close();
126 out = null;
127 lock.release();
128 lock = null;
129 raf.close();
130 raf = null;
131 }
132 catch ( Exception e )
133 {
134 error = e;
135 }
136 finally
137 {
138 try
139 {
140 if ( out != null )
141 {
142 out.close();
143 }
144 }
145 catch ( final IOException e )
146 {
147
148 }
149 finally
150 {
151 try
152 {
153 if ( lock != null )
154 {
155 lock.release();
156 }
157 }
158 catch ( final IOException e )
159 {
160
161 }
162 finally
163 {
164 try
165 {
166 if ( raf != null )
167 {
168 raf.close();
169 }
170 }
171 catch ( final IOException e )
172 {
173
174 }
175 finally
176 {
177 if ( !lockFile.delete() )
178 {
179 lockFile.deleteOnExit();
180 }
181 }
182 }
183 }
184 }
185 }
186
187 }
188
189 private static final boolean PROPER_LOCK_SUPPORT;
190
191 static
192 {
193 String javaVersion = System.getProperty( "java.version" ).trim();
194 boolean notJava5 = !javaVersion.startsWith( "1.5." );
195 String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
196 boolean windows = osName.contains( "windows" );
197 PROPER_LOCK_SUPPORT = notJava5 || windows;
198 }
199
200 private StubRemoteAccessChecker remoteAccessChecker;
201
202 private File dstFile;
203
204 private File partFile;
205
206 private File lockFile;
207
208 private List<Closeable> closeables;
209
210 private PartialFile newPartialFile( long resumeThreshold, int requestTimeout )
211 throws Exception
212 {
213 PartialFile.Factory factory =
214 new PartialFile.Factory( resumeThreshold >= 0L, resumeThreshold, requestTimeout );
215 PartialFile partFile = factory.newInstance( dstFile, remoteAccessChecker );
216 if ( partFile != null )
217 {
218 closeables.add( partFile );
219 }
220 return partFile;
221 }
222
223 @Before
224 public void init()
225 throws Exception
226 {
227 closeables = new ArrayList<>();
228 remoteAccessChecker = new StubRemoteAccessChecker();
229 dstFile = TestFileUtils.createTempFile( "Hello World!" );
230 partFile = new File( dstFile.getPath() + PartialFile.EXT_PART );
231 lockFile = new File( partFile.getPath() + PartialFile.EXT_LOCK );
232 }
233
234 @After
235 public void exit()
236 {
237 for ( Closeable closeable : closeables )
238 {
239 try
240 {
241 closeable.close();
242 }
243 catch ( Exception e )
244 {
245 e.printStackTrace();
246 }
247 }
248 }
249
250 @Test
251 public void testCloseNonResumableFile()
252 throws Exception
253 {
254 PartialFile partialFile = newPartialFile( -1, 100 );
255 assertNotNull( partialFile );
256 assertNotNull( partialFile.getFile() );
257 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
258 partialFile.close();
259 assertFalse( partialFile.getFile().getAbsolutePath(), partialFile.getFile().exists() );
260 }
261
262 @Test
263 public void testCloseResumableFile()
264 throws Exception
265 {
266 PartialFile partialFile = newPartialFile( 0, 100 );
267 assertNotNull( partialFile );
268 assertNotNull( partialFile.getFile() );
269 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
270 assertEquals( partFile, partialFile.getFile() );
271 assertTrue( lockFile.getAbsolutePath(), lockFile.isFile() );
272 partialFile.close();
273 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
274 assertFalse( lockFile.getAbsolutePath(), lockFile.exists() );
275 }
276
277 @Test
278 public void testResumableFileCreationError()
279 throws Exception
280 {
281 assertTrue( partFile.getAbsolutePath(), partFile.mkdirs() );
282 PartialFile partialFile = newPartialFile( 0, 100 );
283 assertNotNull( partialFile );
284 assertFalse( partialFile.isResume() );
285 assertFalse( lockFile.getAbsolutePath(), lockFile.exists() );
286 }
287
288 @Test
289 public void testResumeThreshold()
290 throws Exception
291 {
292 PartialFile partialFile = newPartialFile( 0, 100 );
293 assertNotNull( partialFile );
294 assertTrue( partialFile.isResume() );
295 partialFile.close();
296 partialFile = newPartialFile( 1, 100 );
297 assertNotNull( partialFile );
298 assertFalse( partialFile.isResume() );
299 partialFile.close();
300 }
301
302 @Test( timeout = 10000L )
303 public void testResumeConcurrently_RequestTimeout()
304 throws Exception
305 {
306 assumeTrue( PROPER_LOCK_SUPPORT );
307 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 5 * 1000, 1 );
308 try
309 {
310 newPartialFile( 0, 1000 );
311 fail( "expected exception" );
312 }
313 catch ( Exception e )
314 {
315 assertTrue( e.getMessage().contains( "Timeout" ) );
316 }
317 writer.interrupt();
318 writer.join();
319 }
320
321 @Test( timeout = 10000L )
322 public void testResumeConcurrently_AwaitCompletion_ConcurrentWriterSucceeds()
323 throws Exception
324 {
325 assumeTrue( PROPER_LOCK_SUPPORT );
326 assertTrue( dstFile.setLastModified( System.currentTimeMillis() - 60L * 1000L ) );
327 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 100, 10 );
328 assertNull( newPartialFile( 0, 500 ) );
329 writer.join();
330 assertNull( writer.error );
331 assertEquals( 1, remoteAccessChecker.invocations );
332 }
333
334 @Test( timeout = 10000L )
335 public void testResumeConcurrently_AwaitCompletion_ConcurrentWriterFails()
336 throws Exception
337 {
338 assumeTrue( PROPER_LOCK_SUPPORT );
339 assertTrue( dstFile.setLastModified( System.currentTimeMillis() - 60L * 1000L ) );
340 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 100, -10 );
341 PartialFile partialFile = newPartialFile( 0, 500 );
342 assertNotNull( partialFile );
343 assertTrue( partialFile.isResume() );
344 writer.join();
345 assertNull( writer.error );
346 assertEquals( 1, remoteAccessChecker.invocations );
347 }
348
349 @Test( timeout = 10000L )
350 public void testResumeConcurrently_CheckRemoteAccess()
351 throws Exception
352 {
353 assumeTrue( PROPER_LOCK_SUPPORT );
354 remoteAccessChecker.exception = new IOException( "missing" );
355 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 1000, 1 );
356 try
357 {
358 newPartialFile( 0, 1000 );
359 fail( "expected exception" );
360 }
361 catch ( Exception e )
362 {
363 assertSame( remoteAccessChecker.exception, e );
364 }
365 writer.interrupt();
366 writer.join();
367 }
368
369 }