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.eclipse.aether.internal.test.util.TestLoggerFactory;
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42
43 public class PartialFileTest
44 {
45
46 private static class StubRemoteAccessChecker
47 implements PartialFile.RemoteAccessChecker
48 {
49
50 Exception exception;
51
52 int invocations;
53
54 public void checkRemoteAccess()
55 throws Exception
56 {
57 invocations++;
58 if ( exception != null )
59 {
60 throw exception;
61 }
62 }
63
64 }
65
66 private static class ConcurrentWriter
67 extends Thread
68 {
69
70 private final File dstFile;
71
72 private final File partFile;
73
74 private final File lockFile;
75
76 private final CountDownLatch locked;
77
78 private final int sleep;
79
80 volatile int length;
81
82 Exception error;
83
84 public ConcurrentWriter( File dstFile, int sleep, int length )
85 throws InterruptedException
86 {
87 super( "ConcurrentWriter-" + dstFile.getAbsolutePath() );
88 this.dstFile = dstFile;
89 partFile = new File( dstFile.getPath() + PartialFile.EXT_PART );
90 lockFile = new File( partFile.getPath() + PartialFile.EXT_LOCK );
91 this.sleep = sleep;
92 this.length = length;
93 locked = new CountDownLatch( 1 );
94 start();
95 locked.await();
96 }
97
98 @Override
99 public void run()
100 {
101 RandomAccessFile raf = null;
102 FileLock lock = null;
103 OutputStream out = null;
104 try
105 {
106 raf = new RandomAccessFile( lockFile, "rw" );
107 lock = raf.getChannel().lock( 0, 1, false );
108 locked.countDown();
109 out = new FileOutputStream( partFile );
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 out.write( 65 );
117 out.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 out.close();
127 out = null;
128 lock.release();
129 lock = null;
130 raf.close();
131 raf = null;
132 }
133 catch ( Exception e )
134 {
135 error = e;
136 }
137 finally
138 {
139 try
140 {
141 if ( out != null )
142 {
143 out.close();
144 }
145 }
146 catch ( final IOException e )
147 {
148
149 }
150 finally
151 {
152 try
153 {
154 if ( lock != null )
155 {
156 lock.release();
157 }
158 }
159 catch ( final IOException e )
160 {
161
162 }
163 finally
164 {
165 try
166 {
167 if ( raf != null )
168 {
169 raf.close();
170 }
171 }
172 catch ( final IOException e )
173 {
174
175 }
176 finally
177 {
178 if ( !lockFile.delete() )
179 {
180 lockFile.deleteOnExit();
181 }
182 }
183 }
184 }
185 }
186 }
187
188 }
189
190 private static final boolean PROPER_LOCK_SUPPORT;
191
192 static
193 {
194 String javaVersion = System.getProperty( "java.version" ).trim();
195 boolean notJava5 = !javaVersion.startsWith( "1.5." );
196 String osName = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
197 boolean windows = osName.contains( "windows" );
198 PROPER_LOCK_SUPPORT = notJava5 || windows;
199 }
200
201 private StubRemoteAccessChecker remoteAccessChecker;
202
203 private File dstFile;
204
205 private File partFile;
206
207 private File lockFile;
208
209 private List<Closeable> closeables;
210
211 private PartialFile newPartialFile( long resumeThreshold, int requestTimeout )
212 throws Exception
213 {
214 PartialFile.Factory factory =
215 new PartialFile.Factory( resumeThreshold >= 0L, resumeThreshold, requestTimeout,
216 new TestLoggerFactory().getLogger( "" ) );
217 PartialFile partFile = factory.newInstance( dstFile, remoteAccessChecker );
218 if ( partFile != null )
219 {
220 closeables.add( partFile );
221 }
222 return partFile;
223 }
224
225 @Before
226 public void init()
227 throws Exception
228 {
229 closeables = new ArrayList<Closeable>();
230 remoteAccessChecker = new StubRemoteAccessChecker();
231 dstFile = TestFileUtils.createTempFile( "Hello World!" );
232 partFile = new File( dstFile.getPath() + PartialFile.EXT_PART );
233 lockFile = new File( partFile.getPath() + PartialFile.EXT_LOCK );
234 }
235
236 @After
237 public void exit()
238 {
239 for ( Closeable closeable : closeables )
240 {
241 try
242 {
243 closeable.close();
244 }
245 catch ( Exception e )
246 {
247 e.printStackTrace();
248 }
249 }
250 }
251
252 @Test
253 public void testCloseNonResumableFile()
254 throws Exception
255 {
256 PartialFile partialFile = newPartialFile( -1, 100 );
257 assertNotNull( partialFile );
258 assertNotNull( partialFile.getFile() );
259 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
260 partialFile.close();
261 assertFalse( partialFile.getFile().getAbsolutePath(), partialFile.getFile().exists() );
262 }
263
264 @Test
265 public void testCloseResumableFile()
266 throws Exception
267 {
268 PartialFile partialFile = newPartialFile( 0, 100 );
269 assertNotNull( partialFile );
270 assertNotNull( partialFile.getFile() );
271 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
272 assertEquals( partFile, partialFile.getFile() );
273 assertTrue( lockFile.getAbsolutePath(), lockFile.isFile() );
274 partialFile.close();
275 assertTrue( partialFile.getFile().getAbsolutePath(), partialFile.getFile().isFile() );
276 assertFalse( lockFile.getAbsolutePath(), lockFile.exists() );
277 }
278
279 @Test
280 public void testResumableFileCreationError()
281 throws Exception
282 {
283 assertTrue( partFile.getAbsolutePath(), partFile.mkdirs() );
284 PartialFile partialFile = newPartialFile( 0, 100 );
285 assertNotNull( partialFile );
286 assertFalse( partialFile.isResume() );
287 assertFalse( lockFile.getAbsolutePath(), lockFile.exists() );
288 }
289
290 @Test
291 public void testResumeThreshold()
292 throws Exception
293 {
294 PartialFile partialFile = newPartialFile( 0, 100 );
295 assertNotNull( partialFile );
296 assertTrue( partialFile.isResume() );
297 partialFile.close();
298 partialFile = newPartialFile( 1, 100 );
299 assertNotNull( partialFile );
300 assertFalse( partialFile.isResume() );
301 partialFile.close();
302 }
303
304 @Test( timeout = 10000L )
305 public void testResumeConcurrently_RequestTimeout()
306 throws Exception
307 {
308 assumeTrue( PROPER_LOCK_SUPPORT );
309 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 5 * 1000, 1 );
310 try
311 {
312 newPartialFile( 0, 1000 );
313 fail( "expected exception" );
314 }
315 catch ( Exception e )
316 {
317 assertTrue( e.getMessage().contains( "Timeout" ) );
318 }
319 writer.interrupt();
320 writer.join();
321 }
322
323 @Test( timeout = 10000L )
324 public void testResumeConcurrently_AwaitCompletion_ConcurrentWriterSucceeds()
325 throws Exception
326 {
327 assumeTrue( PROPER_LOCK_SUPPORT );
328 assertTrue( dstFile.setLastModified( System.currentTimeMillis() - 60L * 1000L ) );
329 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 100, 10 );
330 assertNull( newPartialFile( 0, 500 ) );
331 writer.join();
332 assertNull( writer.error );
333 assertEquals( 1, remoteAccessChecker.invocations );
334 }
335
336 @Test( timeout = 10000L )
337 public void testResumeConcurrently_AwaitCompletion_ConcurrentWriterFails()
338 throws Exception
339 {
340 assumeTrue( PROPER_LOCK_SUPPORT );
341 assertTrue( dstFile.setLastModified( System.currentTimeMillis() - 60L * 1000L ) );
342 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 100, -10 );
343 PartialFile partialFile = newPartialFile( 0, 500 );
344 assertNotNull( partialFile );
345 assertTrue( partialFile.isResume() );
346 writer.join();
347 assertNull( writer.error );
348 assertEquals( 1, remoteAccessChecker.invocations );
349 }
350
351 @Test( timeout = 10000L )
352 public void testResumeConcurrently_CheckRemoteAccess()
353 throws Exception
354 {
355 assumeTrue( PROPER_LOCK_SUPPORT );
356 remoteAccessChecker.exception = new IOException( "missing" );
357 ConcurrentWriter writer = new ConcurrentWriter( dstFile, 1000, 1 );
358 try
359 {
360 newPartialFile( 0, 1000 );
361 fail( "expected exception" );
362 }
363 catch ( Exception e )
364 {
365 assertSame( remoteAccessChecker.exception, e );
366 }
367 writer.interrupt();
368 writer.join();
369 }
370
371 }