14
14
import java .io .IOException ;
15
15
import java .net .URI ;
16
16
import java .nio .charset .StandardCharsets ;
17
- import java .nio .file .Files ;
18
- import java .nio .file .Path ;
19
- import java .nio .file .Paths ;
17
+ import java .nio .file .*;
20
18
import java .security .MessageDigest ;
19
+ import java .time .Duration ;
20
+ import java .time .Instant ;
21
+ import java .util .concurrent .TimeUnit ;
22
+
23
+ import static java .nio .file .StandardWatchEventKinds .ENTRY_CREATE ;
21
24
22
25
public class ExtractArchiveStep implements LocalToolInstallationStep {
23
26
27
+ public static final Duration OTHER_PROCESS_ARCHIVE_DOWNLOAD_WAIT_LIMIT = Duration .ofMinutes (5 );
24
28
private final String rawArchiveUri ;
25
29
26
30
public ExtractArchiveStep (String rawArchiveUri ) {
@@ -52,18 +56,33 @@ private Path downloadArchive() throws LocalToolInstallationStepException {
52
56
return archiveCachePath ;
53
57
}
54
58
59
+ Path archiveDownloadingPath = getArchiveDownloadingPath ();
60
+ if (Files .exists (archiveDownloadingPath )) {
61
+ waitUntilArchiveHasBeenDownloadedByOtherProcess (archiveCachePath );
62
+ ProcessOutput .writeDebugMessage ("using cached archive from {0}" , archiveCachePath );
63
+ return archiveCachePath ;
64
+ }
65
+
55
66
ProcessOutput .writeDebugMessage ("downloading archive from {0}" , rawArchiveUri );
56
67
try (var inputStream = URI .create (rawArchiveUri ).toURL ().openStream ();
57
- var outputStream = new FileOutputStream (archiveCachePath .toFile ())) {
68
+ var outputStream = new FileOutputStream (archiveDownloadingPath .toFile ())) {
58
69
59
70
IOUtils .copy (inputStream , outputStream );
71
+ } catch (IOException e ) {
72
+ deleteIfExists (archiveDownloadingPath );
73
+ throw new LocalToolInstallationStepException ("failed to download archive from URI " + rawArchiveUri , e );
74
+ }
60
75
76
+ try {
77
+ Files .move (archiveDownloadingPath , archiveCachePath );
61
78
ProcessOutput .writeDebugMessage ("cached archive at {0}" , archiveCachePath );
62
- return archiveCachePath ;
63
79
} catch (IOException e ) {
80
+ deleteIfExists (archiveDownloadingPath );
64
81
deleteIfExists (archiveCachePath );
65
- throw new LocalToolInstallationStepException ("failed to download archive from URI " + rawArchiveUri , e );
82
+ throw new LocalToolInstallationStepException ("failed to move downloaded archive from " + archiveDownloadingPath + " to " + archiveCachePath , e );
66
83
}
84
+
85
+ return archiveCachePath ;
67
86
}
68
87
69
88
private Path getArchiveCachePath () throws LocalToolInstallationStepException {
@@ -82,6 +101,43 @@ private Path getCacheDirectory() throws IOException {
82
101
});
83
102
}
84
103
104
+ private Path getArchiveDownloadingPath () throws LocalToolInstallationStepException {
105
+ Path archiveCachePath = getArchiveCachePath ();
106
+ return archiveCachePath .getParent ().resolve (archiveCachePath .getFileName () + ".downloading" );
107
+ }
108
+
109
+ private void waitUntilArchiveHasBeenDownloadedByOtherProcess (Path archivePath ) throws LocalToolInstallationStepException {
110
+ try (WatchService watchService = createWatchServiceForArchive (archivePath )) {
111
+ Instant startTime = Instant .now ();
112
+ while (!Files .exists (archivePath ) && Duration .between (startTime , Instant .now ()).compareTo (OTHER_PROCESS_ARCHIVE_DOWNLOAD_WAIT_LIMIT ) <= 0 ) {
113
+ waitForArchiveDownloadEvents (watchService );
114
+ }
115
+ } catch (Exception e ) {
116
+ ProcessOutput .writeDebugMessage ("Got exception while waiting for archive to be downloaded by other process" , e );
117
+ }
118
+
119
+ if (!Files .exists (archivePath )) {
120
+ throw new LocalToolInstallationStepException ("failed to wait for archive to be downloaded" );
121
+ }
122
+ }
123
+
124
+ private WatchService createWatchServiceForArchive (Path archiveDownloadingPath ) throws IOException {
125
+ WatchService watchService = FileSystems .getDefault ().newWatchService ();
126
+ archiveDownloadingPath .getParent ().register (watchService , ENTRY_CREATE );
127
+
128
+ return watchService ;
129
+ }
130
+
131
+ private void waitForArchiveDownloadEvents (WatchService watchService ) throws Exception {
132
+ WatchKey key = watchService .poll (OTHER_PROCESS_ARCHIVE_DOWNLOAD_WAIT_LIMIT .toMinutes (), TimeUnit .MINUTES );
133
+ if (key != null ) {
134
+ key .pollEvents ();
135
+ if (!key .reset ()) {
136
+ throw new IOException ("Watch key no more valid" );
137
+ }
138
+ }
139
+ }
140
+
85
141
private void extractArchive (Path localArchivePath , File installationRoot ) throws LocalToolInstallationStepException {
86
142
try {
87
143
ArchiveExtractorFactory .createArchiveExtractor ().extractArchive (localArchivePath .toFile (), installationRoot );
0 commit comments