View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.transport.file;
20  
21  import java.io.FileNotFoundException;
22  import java.net.URI;
23  import java.nio.charset.StandardCharsets;
24  import java.nio.file.FileSystem;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.Paths;
28  
29  import com.google.common.jimfs.Configuration;
30  import com.google.common.jimfs.Jimfs;
31  import org.eclipse.aether.DefaultRepositorySystemSession;
32  import org.eclipse.aether.internal.test.util.TestFileUtils;
33  import org.eclipse.aether.internal.test.util.TestUtils;
34  import org.eclipse.aether.repository.RemoteRepository;
35  import org.eclipse.aether.spi.connector.transport.GetTask;
36  import org.eclipse.aether.spi.connector.transport.PeekTask;
37  import org.eclipse.aether.spi.connector.transport.PutTask;
38  import org.eclipse.aether.spi.connector.transport.Transporter;
39  import org.eclipse.aether.spi.connector.transport.TransporterFactory;
40  import org.eclipse.aether.transfer.NoTransporterException;
41  import org.eclipse.aether.transfer.TransferCancelledException;
42  import org.junit.jupiter.api.AfterEach;
43  import org.junit.jupiter.api.Assertions;
44  import org.junit.jupiter.api.Test;
45  import org.junit.jupiter.params.ParameterizedTest;
46  import org.junit.jupiter.params.provider.EnumSource;
47  
48  import static org.junit.jupiter.api.Assertions.*;
49  
50  /**
51   */
52  public class FileTransporterTest {
53  
54      private DefaultRepositorySystemSession session;
55  
56      private TransporterFactory factory;
57  
58      private Transporter transporter;
59  
60      private Path repoDir;
61  
62      private FileSystem fileSystem;
63  
64      enum FS {
65          DEFAULT,
66          JIMFS
67      }
68  
69      private RemoteRepository newRepo(String url) {
70          return new RemoteRepository.Builder("test", "default", url).build();
71      }
72  
73      private void newTransporter(String url) throws Exception {
74          if (transporter != null) {
75              transporter.close();
76              transporter = null;
77          }
78          if (factory == null) {
79              factory = new FileTransporterFactory();
80          }
81          if (session == null) {
82              session = TestUtils.newSession();
83          }
84          transporter = factory.newInstance(session, newRepo(url));
85      }
86  
87      void setUp(FS fs) {
88          try {
89              fileSystem = fs == FS.JIMFS ? Jimfs.newFileSystem() : null;
90              repoDir = fileSystem == null ? TestFileUtils.createTempDir().toPath() : fileSystem.getPath(".");
91              Files.write(repoDir.resolve("file.txt"), "test".getBytes(StandardCharsets.UTF_8));
92              Files.write(repoDir.resolve("empty.txt"), "".getBytes(StandardCharsets.UTF_8));
93              Files.write(repoDir.resolve("some space.txt"), "space".getBytes(StandardCharsets.UTF_8));
94              newTransporter(repoDir.toUri().toASCIIString());
95          } catch (Exception e) {
96              Assertions.fail(e);
97          }
98      }
99  
100     @AfterEach
101     void tearDown() throws Exception {
102         if (transporter != null) {
103             transporter.close();
104             transporter = null;
105         }
106         if (fileSystem != null) {
107             fileSystem.close();
108         }
109         factory = null;
110         session = null;
111     }
112 
113     @ParameterizedTest
114     @EnumSource(FS.class)
115     void testClassify(FS fs) {
116         setUp(fs);
117         assertEquals(Transporter.ERROR_OTHER, transporter.classify(new FileNotFoundException()));
118         assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(new ResourceNotFoundException("test")));
119     }
120 
121     @ParameterizedTest
122     @EnumSource(FS.class)
123     void testPeek(FS fs) throws Exception {
124         setUp(fs);
125         transporter.peek(new PeekTask(URI.create("file.txt")));
126     }
127 
128     @ParameterizedTest
129     @EnumSource(FS.class)
130     void testPeek_NotFound(FS fs) throws Exception {
131         setUp(fs);
132         try {
133             transporter.peek(new PeekTask(URI.create("missing.txt")));
134             fail("Expected error");
135         } catch (ResourceNotFoundException e) {
136             assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
137         }
138     }
139 
140     @ParameterizedTest
141     @EnumSource(FS.class)
142     void testPeek_Closed(FS fs) throws Exception {
143         setUp(fs);
144         transporter.close();
145         try {
146             transporter.peek(new PeekTask(URI.create("missing.txt")));
147             fail("Expected error");
148         } catch (IllegalStateException e) {
149             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
150         }
151     }
152 
153     @ParameterizedTest
154     @EnumSource(FS.class)
155     void testGet_ToMemory(FS fs) throws Exception {
156         setUp(fs);
157         RecordingTransportListener listener = new RecordingTransportListener();
158         GetTask task = new GetTask(URI.create("file.txt")).setListener(listener);
159         transporter.get(task);
160         assertEquals("test", task.getDataString());
161         assertEquals(0L, listener.dataOffset);
162         assertEquals(4L, listener.dataLength);
163         assertEquals(1, listener.startedCount);
164         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
165         assertEquals(task.getDataString(), new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
166     }
167 
168     @ParameterizedTest
169     @EnumSource(FS.class)
170     void testGet_ToFile(FS fs) throws Exception {
171         setUp(fs);
172         Path file = TestFileUtils.createTempFile("failure").toPath();
173         RecordingTransportListener listener = new RecordingTransportListener();
174         GetTask task = new GetTask(URI.create("file.txt")).setDataPath(file).setListener(listener);
175         transporter.get(task);
176         assertEquals("test", TestFileUtils.readString(file.toFile()));
177         assertEquals(0L, listener.dataOffset);
178         assertEquals(4L, listener.dataLength);
179         assertEquals(1, listener.startedCount);
180         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
181         assertEquals("test", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
182     }
183 
184     @ParameterizedTest
185     @EnumSource(FS.class)
186     void testGet_EmptyResource(FS fs) throws Exception {
187         setUp(fs);
188         Path file = TestFileUtils.createTempFile("failure").toPath();
189         RecordingTransportListener listener = new RecordingTransportListener();
190         GetTask task = new GetTask(URI.create("empty.txt")).setDataPath(file).setListener(listener);
191         transporter.get(task);
192         assertEquals("", TestFileUtils.readString(file.toFile()));
193         assertEquals(0L, listener.dataOffset);
194         assertEquals(0L, listener.dataLength);
195         assertEquals(1, listener.startedCount);
196         assertEquals(0, listener.progressedCount);
197         assertEquals("", new String(listener.baos.toByteArray(), StandardCharsets.UTF_8));
198     }
199 
200     @ParameterizedTest
201     @EnumSource(FS.class)
202     void testGet_EncodedResourcePath(FS fs) throws Exception {
203         setUp(fs);
204         GetTask task = new GetTask(URI.create("some%20space.txt"));
205         transporter.get(task);
206         assertEquals("space", task.getDataString());
207     }
208 
209     @ParameterizedTest
210     @EnumSource(FS.class)
211     void testGet_Fragment(FS fs) throws Exception {
212         setUp(fs);
213         GetTask task = new GetTask(URI.create("file.txt#ignored"));
214         transporter.get(task);
215         assertEquals("test", task.getDataString());
216     }
217 
218     @ParameterizedTest
219     @EnumSource(FS.class)
220     void testGet_Query(FS fs) throws Exception {
221         setUp(fs);
222         GetTask task = new GetTask(URI.create("file.txt?ignored"));
223         transporter.get(task);
224         assertEquals("test", task.getDataString());
225     }
226 
227     @ParameterizedTest
228     @EnumSource(FS.class)
229     void testGet_FileHandleLeak(FS fs) throws Exception {
230         setUp(fs);
231         for (int i = 0; i < 100; i++) {
232             Path file = TestFileUtils.createTempFile("failure").toPath();
233             transporter.get(new GetTask(URI.create("file.txt")).setDataPath(file));
234             assertTrue(file.toFile().delete(), i + ", " + file.toFile().getAbsolutePath());
235         }
236     }
237 
238     @ParameterizedTest
239     @EnumSource(FS.class)
240     void testGet_NotFound(FS fs) throws Exception {
241         setUp(fs);
242         try {
243             transporter.get(new GetTask(URI.create("missing.txt")));
244             fail("Expected error");
245         } catch (ResourceNotFoundException e) {
246             assertEquals(Transporter.ERROR_NOT_FOUND, transporter.classify(e));
247         }
248     }
249 
250     @ParameterizedTest
251     @EnumSource(FS.class)
252     void testGet_Closed(FS fs) throws Exception {
253         setUp(fs);
254         transporter.close();
255         try {
256             transporter.get(new GetTask(URI.create("file.txt")));
257             fail("Expected error");
258         } catch (IllegalStateException e) {
259             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
260         }
261     }
262 
263     @ParameterizedTest
264     @EnumSource(FS.class)
265     void testGet_StartCancelled(FS fs) throws Exception {
266         setUp(fs);
267         RecordingTransportListener listener = new RecordingTransportListener();
268         listener.cancelStart = true;
269         GetTask task = new GetTask(URI.create("file.txt")).setListener(listener);
270         try {
271             transporter.get(task);
272             fail("Expected error");
273         } catch (TransferCancelledException e) {
274             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
275         }
276         assertEquals(0L, listener.dataOffset);
277         assertEquals(4L, listener.dataLength);
278         assertEquals(1, listener.startedCount);
279         assertEquals(0, listener.progressedCount);
280     }
281 
282     @ParameterizedTest
283     @EnumSource(FS.class)
284     void testGet_ProgressCancelled(FS fs) throws Exception {
285         setUp(fs);
286         RecordingTransportListener listener = new RecordingTransportListener();
287         listener.cancelProgress = true;
288         GetTask task = new GetTask(URI.create("file.txt")).setListener(listener);
289         try {
290             transporter.get(task);
291             fail("Expected error");
292         } catch (TransferCancelledException e) {
293             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
294         }
295         assertEquals(0L, listener.dataOffset);
296         assertEquals(4L, listener.dataLength);
297         assertEquals(1, listener.startedCount);
298         assertEquals(1, listener.progressedCount);
299     }
300 
301     @ParameterizedTest
302     @EnumSource(FS.class)
303     void testPut_FromMemory(FS fs) throws Exception {
304         setUp(fs);
305         RecordingTransportListener listener = new RecordingTransportListener();
306         PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload");
307         transporter.put(task);
308         assertEquals(0L, listener.dataOffset);
309         assertEquals(6L, listener.dataLength);
310         assertEquals(1, listener.startedCount);
311         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
312         assertEquals("upload", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8));
313     }
314 
315     @ParameterizedTest
316     @EnumSource(FS.class)
317     void testPut_FromFile(FS fs) throws Exception {
318         setUp(fs);
319         Path file = TestFileUtils.createTempFile("upload").toPath();
320         RecordingTransportListener listener = new RecordingTransportListener();
321         PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataPath(file);
322         transporter.put(task);
323         assertEquals(0L, listener.dataOffset);
324         assertEquals(6L, listener.dataLength);
325         assertEquals(1, listener.startedCount);
326         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
327         assertEquals("upload", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8));
328     }
329 
330     @ParameterizedTest
331     @EnumSource(FS.class)
332     void testPut_EmptyResource(FS fs) throws Exception {
333         setUp(fs);
334         RecordingTransportListener listener = new RecordingTransportListener();
335         PutTask task = new PutTask(URI.create("file.txt")).setListener(listener);
336         transporter.put(task);
337         assertEquals(0L, listener.dataOffset);
338         assertEquals(0L, listener.dataLength);
339         assertEquals(1, listener.startedCount);
340         assertEquals(0, listener.progressedCount);
341         assertEquals("", new String(Files.readAllBytes(repoDir.resolve("file.txt")), StandardCharsets.UTF_8));
342     }
343 
344     @ParameterizedTest
345     @EnumSource(FS.class)
346     void testPut_NonExistentParentDir(FS fs) throws Exception {
347         setUp(fs);
348         RecordingTransportListener listener = new RecordingTransportListener();
349         PutTask task = new PutTask(URI.create("dir/sub/dir/file.txt"))
350                 .setListener(listener)
351                 .setDataString("upload");
352         transporter.put(task);
353         assertEquals(0L, listener.dataOffset);
354         assertEquals(6L, listener.dataLength);
355         assertEquals(1, listener.startedCount);
356         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
357         assertEquals(
358                 "upload",
359                 new String(Files.readAllBytes(repoDir.resolve("dir/sub/dir/file.txt")), StandardCharsets.UTF_8));
360     }
361 
362     @ParameterizedTest
363     @EnumSource(FS.class)
364     void testPut_EncodedResourcePath(FS fs) throws Exception {
365         setUp(fs);
366         RecordingTransportListener listener = new RecordingTransportListener();
367         PutTask task = new PutTask(URI.create("some%20space.txt"))
368                 .setListener(listener)
369                 .setDataString("OK");
370         transporter.put(task);
371         assertEquals(0L, listener.dataOffset);
372         assertEquals(2L, listener.dataLength);
373         assertEquals(1, listener.startedCount);
374         assertTrue(listener.progressedCount > 0, "Count: " + listener.progressedCount);
375         assertEquals("OK", new String(Files.readAllBytes(repoDir.resolve("some space.txt")), StandardCharsets.UTF_8));
376     }
377 
378     @ParameterizedTest
379     @EnumSource(FS.class)
380     void testPut_FileHandleLeak(FS fs) throws Exception {
381         setUp(fs);
382         for (int i = 0; i < 100; i++) {
383             Path src = TestFileUtils.createTempFile("upload").toPath();
384             Path dst = repoDir.resolve("file.txt");
385             transporter.put(new PutTask(URI.create("file.txt")).setDataPath(src));
386             assertTrue(src.toFile().delete(), i + ", " + src.toFile().getAbsolutePath());
387             assertTrue(Files.deleteIfExists(dst), i + ", " + dst.toAbsolutePath());
388         }
389     }
390 
391     @ParameterizedTest
392     @EnumSource(FS.class)
393     void testPut_Closed(FS fs) throws Exception {
394         setUp(fs);
395         transporter.close();
396         try {
397             transporter.put(new PutTask(URI.create("missing.txt")));
398             fail("Expected error");
399         } catch (IllegalStateException e) {
400             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
401         }
402     }
403 
404     @ParameterizedTest
405     @EnumSource(FS.class)
406     void testPut_StartCancelled(FS fs) throws Exception {
407         setUp(fs);
408         RecordingTransportListener listener = new RecordingTransportListener();
409         listener.cancelStart = true;
410         PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload");
411         try {
412             transporter.put(task);
413             fail("Expected error");
414         } catch (TransferCancelledException e) {
415             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
416         }
417         assertEquals(0L, listener.dataOffset);
418         assertEquals(6L, listener.dataLength);
419         assertEquals(1, listener.startedCount);
420         assertEquals(0, listener.progressedCount);
421         assertFalse(Files.exists(repoDir.resolve("file.txt")));
422     }
423 
424     @ParameterizedTest
425     @EnumSource(FS.class)
426     void testPut_ProgressCancelled(FS fs) throws Exception {
427         setUp(fs);
428         RecordingTransportListener listener = new RecordingTransportListener();
429         listener.cancelProgress = true;
430         PutTask task = new PutTask(URI.create("file.txt")).setListener(listener).setDataString("upload");
431         try {
432             transporter.put(task);
433             fail("Expected error");
434         } catch (TransferCancelledException e) {
435             assertEquals(Transporter.ERROR_OTHER, transporter.classify(e));
436         }
437         assertEquals(0L, listener.dataOffset);
438         assertEquals(6L, listener.dataLength);
439         assertEquals(1, listener.startedCount);
440         assertEquals(1, listener.progressedCount);
441         assertFalse(Files.exists(repoDir.resolve("file.txt")));
442     }
443 
444     @Test
445     void testInit_BadProtocol() {
446         assertThrows(NoTransporterException.class, () -> newTransporter("bad:/void"));
447     }
448 
449     @Test
450     void testInit_CaseInsensitiveProtocol() throws Exception {
451         newTransporter("file:/void");
452         newTransporter("FILE:/void");
453         newTransporter("File:/void");
454     }
455 
456     @Test
457     void testInit_OpaqueUrl() throws Exception {
458         testInit("file:repository", "repository");
459     }
460 
461     @Test
462     void testInit_OpaqueUrlTrailingSlash() throws Exception {
463         testInit("file:repository/", "repository");
464     }
465 
466     @Test
467     void testInit_OpaqueUrlSpaces() throws Exception {
468         testInit("file:repo%20space", "repo space");
469     }
470 
471     @Test
472     void testInit_OpaqueUrlSpacesDecoded() throws Exception {
473         testInit("file:repo space", "repo space");
474     }
475 
476     @Test
477     void testInit_HierarchicalUrl() throws Exception {
478         testInit("file:/repository", "/repository");
479     }
480 
481     @Test
482     void testInit_HierarchicalUrlTrailingSlash() throws Exception {
483         testInit("file:/repository/", "/repository");
484     }
485 
486     @Test
487     void testInit_HierarchicalUrlSpaces() throws Exception {
488         testInit("file:/repo%20space", "/repo space");
489     }
490 
491     @Test
492     void testInit_HierarchicalUrlSpacesDecoded() throws Exception {
493         testInit("file:/repo space", "/repo space");
494     }
495 
496     @Test
497     void testInit_HierarchicalUrlRoot() throws Exception {
498         testInit("file:/", "/");
499     }
500 
501     @Test
502     void testInit_HierarchicalUrlHostNoPath() throws Exception {
503         testInit("file://host/", "/");
504     }
505 
506     @Test
507     void testInit_HierarchicalUrlHostPath() throws Exception {
508         testInit("file://host/dir", "/dir");
509     }
510 
511     @Test
512     void testInit_NonDefaultFileSystemRelative() throws Exception {
513         try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
514             Path path = fs.getPath("dir");
515             testInit(path.toUri().toASCIIString(), "/work/dir");
516         }
517     }
518 
519     @Test
520     void testInit_NonDefaultFileSystemAbsolute() throws Exception {
521         try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix())) {
522             Path path = fs.getPath("/dir");
523             testInit(path.toUri().toASCIIString(), "/dir");
524         }
525     }
526 
527     private void testInit(String base, String expected) throws Exception {
528         newTransporter(base);
529         String exp = expected;
530         if (base.startsWith("file:")) {
531             // on def FileSystem we do extra dance that we do NOT do in case of non-default File Systems:
532             // like accepting weird URLs/URIs and resolving/abs against CWD, that may not be defined in case
533             // of non-default FileSystems (OTOH, they MAY do it, like JIMFS has $cwd="/work")
534             exp = Paths.get(expected).toAbsolutePath().toString();
535         }
536         // compare path string representation only, as otherwise (Object equality) it would fail
537         // if we end up with non default FS for example
538         assertEquals(exp, ((FileTransporter) transporter).getBasePath().toString());
539     }
540 }