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 java.io.File;
23 import java.io.IOException;
24 import java.net.URI;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Map;
29 import java.util.UUID;
30
31 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
32 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
33 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind;
34 import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation;
35 import org.eclipse.aether.spi.io.FileProcessor;
36 import org.eclipse.aether.transfer.ChecksumFailureException;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40
41
42
43 final class ChecksumValidator
44 {
45
46 interface ChecksumFetcher
47 {
48
49
50
51
52
53 boolean fetchChecksum( URI remote, File local )
54 throws Exception;
55
56 }
57
58 private static final Logger LOGGER = LoggerFactory.getLogger( ChecksumValidator.class );
59
60 private final File dataFile;
61
62 private final Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories;
63
64 private final Collection<File> tempFiles;
65
66 private final FileProcessor fileProcessor;
67
68 private final ChecksumFetcher checksumFetcher;
69
70 private final ChecksumPolicy checksumPolicy;
71
72 private final Map<String, String> providedChecksums;
73
74 private final Collection<ChecksumLocation> checksumLocations;
75
76 private final Map<File, Object> checksumFiles;
77
78 ChecksumValidator( File dataFile,
79 Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
80 FileProcessor fileProcessor,
81 ChecksumFetcher checksumFetcher,
82 ChecksumPolicy checksumPolicy,
83 Map<String, String> providedChecksums,
84 Collection<ChecksumLocation> checksumLocations )
85 {
86 this.dataFile = dataFile;
87 this.checksumAlgorithmFactories = checksumAlgorithmFactories;
88 this.tempFiles = new HashSet<>();
89 this.fileProcessor = fileProcessor;
90 this.checksumFetcher = checksumFetcher;
91 this.checksumPolicy = checksumPolicy;
92 this.providedChecksums = providedChecksums;
93 this.checksumLocations = checksumLocations;
94 this.checksumFiles = new HashMap<>();
95 }
96
97 public ChecksumCalculator newChecksumCalculator( File targetFile )
98 {
99 if ( checksumPolicy != null )
100 {
101 return ChecksumCalculator.newInstance( targetFile, checksumAlgorithmFactories );
102 }
103 return null;
104 }
105
106 public void validate( Map<String, ?> actualChecksums, Map<String, ?> includedChecksums )
107 throws ChecksumFailureException
108 {
109 if ( checksumPolicy == null )
110 {
111 return;
112 }
113 if ( providedChecksums != null
114 && validateChecksums( actualChecksums, ChecksumKind.PROVIDED, providedChecksums ) )
115 {
116 return;
117 }
118 if ( includedChecksums != null
119 && validateChecksums( actualChecksums, ChecksumKind.REMOTE_INCLUDED, includedChecksums ) )
120 {
121 return;
122 }
123 if ( !checksumLocations.isEmpty() )
124 {
125 if ( validateExternalChecksums( actualChecksums ) )
126 {
127 return;
128 }
129 checksumPolicy.onNoMoreChecksums();
130 }
131 }
132
133 private boolean validateChecksums( Map<String, ?> actualChecksums, ChecksumKind kind, Map<String, ?> checksums )
134 throws ChecksumFailureException
135 {
136 for ( Map.Entry<String, ?> entry : checksums.entrySet() )
137 {
138 String algo = entry.getKey();
139 Object calculated = actualChecksums.get( algo );
140 if ( !( calculated instanceof String ) )
141 {
142 continue;
143 }
144 ChecksumAlgorithmFactory checksumAlgorithmFactory = checksumAlgorithmFactories.stream()
145 .filter( a -> a.getName().equals( algo ) )
146 .findFirst()
147 .orElse( null );
148 if ( checksumAlgorithmFactory == null )
149 {
150 continue;
151 }
152
153 String actual = String.valueOf( calculated );
154 String expected = entry.getValue().toString();
155 checksumFiles.put( getChecksumFile( checksumAlgorithmFactory ), expected );
156
157 if ( !isEqualChecksum( expected, actual ) )
158 {
159 checksumPolicy.onChecksumMismatch( checksumAlgorithmFactory.getName(), kind,
160 new ChecksumFailureException( expected, kind.name(), actual )
161 );
162 }
163 else if ( checksumPolicy.onChecksumMatch( checksumAlgorithmFactory.getName(), kind ) )
164 {
165 return true;
166 }
167 }
168 return false;
169 }
170
171 private boolean validateExternalChecksums( Map<String, ?> actualChecksums )
172 throws ChecksumFailureException
173 {
174 for ( ChecksumLocation checksumLocation : checksumLocations )
175 {
176 ChecksumAlgorithmFactory factory = checksumLocation.getChecksumAlgorithmFactory();
177 Object calculated = actualChecksums.get( factory.getName() );
178 if ( calculated instanceof Exception )
179 {
180 checksumPolicy.onChecksumError(
181 factory.getName(), ChecksumKind.REMOTE_EXTERNAL,
182 new ChecksumFailureException( (Exception) calculated )
183 );
184 continue;
185 }
186 try
187 {
188 File checksumFile = getChecksumFile( checksumLocation.getChecksumAlgorithmFactory() );
189 File tmp = createTempFile( checksumFile );
190 try
191 {
192 if ( !checksumFetcher.fetchChecksum(
193 checksumLocation.getLocation(), tmp
194 ) )
195 {
196 continue;
197 }
198 }
199 catch ( Exception e )
200 {
201 checksumPolicy.onChecksumError(
202 factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException( e )
203 );
204 continue;
205 }
206
207 String actual = String.valueOf( calculated );
208 String expected = fileProcessor.readChecksum( tmp );
209 checksumFiles.put( checksumFile, tmp );
210
211 if ( !isEqualChecksum( expected, actual ) )
212 {
213 checksumPolicy.onChecksumMismatch(
214 factory.getName(), ChecksumKind.REMOTE_EXTERNAL,
215 new ChecksumFailureException( expected, ChecksumKind.REMOTE_EXTERNAL.name(), actual )
216 );
217 }
218 else if ( checksumPolicy.onChecksumMatch( factory.getName(), ChecksumKind.REMOTE_EXTERNAL ) )
219 {
220 return true;
221 }
222 }
223 catch ( IOException e )
224 {
225 checksumPolicy.onChecksumError(
226 factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException( e )
227 );
228 }
229 }
230 return false;
231 }
232
233 private static boolean isEqualChecksum( String expected, String actual )
234 {
235 return expected.equalsIgnoreCase( actual );
236 }
237
238 private File getChecksumFile( ChecksumAlgorithmFactory factory )
239 {
240 return new File( dataFile.getPath() + '.' + factory.getFileExtension() );
241 }
242
243 private File createTempFile( File path )
244 throws IOException
245 {
246 File file =
247 File.createTempFile( path.getName() + "-"
248 + UUID.randomUUID().toString().replace( "-", "" ).substring( 0, 8 ), ".tmp", path.getParentFile() );
249 tempFiles.add( file );
250 return file;
251 }
252
253 private void clearTempFiles()
254 {
255 for ( File file : tempFiles )
256 {
257 if ( !file.delete() && file.exists() )
258 {
259 LOGGER.debug( "Could not delete temporary file {}", file );
260 }
261 }
262 tempFiles.clear();
263 }
264
265 public void retry()
266 {
267 checksumPolicy.onTransferRetry();
268 checksumFiles.clear();
269 clearTempFiles();
270 }
271
272 public boolean handle( ChecksumFailureException exception )
273 {
274 return checksumPolicy.onTransferChecksumFailure( exception );
275 }
276
277 public void commit()
278 {
279 for ( Map.Entry<File, Object> entry : checksumFiles.entrySet() )
280 {
281 File checksumFile = entry.getKey();
282 Object tmp = entry.getValue();
283 try
284 {
285 if ( tmp instanceof File )
286 {
287 fileProcessor.move( (File) tmp, checksumFile );
288 tempFiles.remove( tmp );
289 }
290 else
291 {
292 fileProcessor.writeChecksum( checksumFile, String.valueOf( tmp ) );
293 }
294 }
295 catch ( IOException e )
296 {
297 LOGGER.debug( "Failed to write checksum file {}", checksumFile, e );
298 }
299 }
300 checksumFiles.clear();
301 }
302
303 public void close()
304 {
305 clearTempFiles();
306 }
307
308 }