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.io.UncheckedIOException;
25 import java.net.URI;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.Map;
29
30 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
31 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
32 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind;
33 import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation;
34 import org.eclipse.aether.spi.io.FileProcessor;
35 import org.eclipse.aether.transfer.ChecksumFailureException;
36 import org.eclipse.aether.util.FileUtils;
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 FileProcessor fileProcessor;
65
66 private final ChecksumFetcher checksumFetcher;
67
68 private final ChecksumPolicy checksumPolicy;
69
70 private final Map<String, String> providedChecksums;
71
72 private final Collection<ChecksumLocation> checksumLocations;
73
74 private final Map<File, String> checksumExpectedValues;
75
76 ChecksumValidator( File dataFile,
77 Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
78 FileProcessor fileProcessor,
79 ChecksumFetcher checksumFetcher,
80 ChecksumPolicy checksumPolicy,
81 Map<String, String> providedChecksums,
82 Collection<ChecksumLocation> checksumLocations )
83 {
84 this.dataFile = dataFile;
85 this.checksumAlgorithmFactories = checksumAlgorithmFactories;
86 this.fileProcessor = fileProcessor;
87 this.checksumFetcher = checksumFetcher;
88 this.checksumPolicy = checksumPolicy;
89 this.providedChecksums = providedChecksums;
90 this.checksumLocations = checksumLocations;
91 this.checksumExpectedValues = new HashMap<>();
92 }
93
94 public ChecksumCalculator newChecksumCalculator( File targetFile )
95 {
96 if ( checksumPolicy != null )
97 {
98 return ChecksumCalculator.newInstance( targetFile, checksumAlgorithmFactories );
99 }
100 return null;
101 }
102
103 public void validate( Map<String, ?> actualChecksums, Map<String, ?> includedChecksums )
104 throws ChecksumFailureException
105 {
106 if ( checksumPolicy == null )
107 {
108 return;
109 }
110 if ( providedChecksums != null
111 && validateChecksums( actualChecksums, ChecksumKind.PROVIDED, providedChecksums ) )
112 {
113 return;
114 }
115 if ( includedChecksums != null
116 && validateChecksums( actualChecksums, ChecksumKind.REMOTE_INCLUDED, includedChecksums ) )
117 {
118 return;
119 }
120 if ( !checksumLocations.isEmpty() )
121 {
122 if ( validateExternalChecksums( actualChecksums ) )
123 {
124 return;
125 }
126 checksumPolicy.onNoMoreChecksums();
127 }
128 }
129
130 private boolean validateChecksums( Map<String, ?> actualChecksums, ChecksumKind kind, Map<String, ?> checksums )
131 throws ChecksumFailureException
132 {
133 for ( Map.Entry<String, ?> entry : checksums.entrySet() )
134 {
135 String algo = entry.getKey();
136 Object calculated = actualChecksums.get( algo );
137 if ( !( calculated instanceof String ) )
138 {
139 continue;
140 }
141 ChecksumAlgorithmFactory checksumAlgorithmFactory = checksumAlgorithmFactories.stream()
142 .filter( a -> a.getName().equals( algo ) )
143 .findFirst()
144 .orElse( null );
145 if ( checksumAlgorithmFactory == null )
146 {
147 continue;
148 }
149
150 String actual = String.valueOf( calculated );
151 String expected = entry.getValue().toString();
152 checksumExpectedValues.put( getChecksumFile( checksumAlgorithmFactory ), expected );
153
154 if ( !isEqualChecksum( expected, actual ) )
155 {
156 checksumPolicy.onChecksumMismatch( checksumAlgorithmFactory.getName(), kind,
157 new ChecksumFailureException( expected, kind.name(), actual )
158 );
159 }
160 else if ( checksumPolicy.onChecksumMatch( checksumAlgorithmFactory.getName(), kind ) )
161 {
162 return true;
163 }
164 }
165 return false;
166 }
167
168 private boolean validateExternalChecksums( Map<String, ?> actualChecksums )
169 throws ChecksumFailureException
170 {
171 for ( ChecksumLocation checksumLocation : checksumLocations )
172 {
173 ChecksumAlgorithmFactory factory = checksumLocation.getChecksumAlgorithmFactory();
174 Object calculated = actualChecksums.get( factory.getName() );
175 if ( calculated instanceof Exception )
176 {
177 checksumPolicy.onChecksumError(
178 factory.getName(), ChecksumKind.REMOTE_EXTERNAL,
179 new ChecksumFailureException( (Exception) calculated )
180 );
181 continue;
182 }
183 File checksumFile = getChecksumFile( checksumLocation.getChecksumAlgorithmFactory() );
184 try ( FileUtils.TempFile tempFile = FileUtils.newTempFile( checksumFile.toPath() ) )
185 {
186 File tmp = tempFile.getPath().toFile();
187 try
188 {
189 if ( !checksumFetcher.fetchChecksum(
190 checksumLocation.getLocation(), tmp
191 ) )
192 {
193 continue;
194 }
195 }
196 catch ( Exception e )
197 {
198 checksumPolicy.onChecksumError(
199 factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException( e )
200 );
201 continue;
202 }
203
204 String actual = String.valueOf( calculated );
205 String expected = fileProcessor.readChecksum( tmp );
206 checksumExpectedValues.put( checksumFile, expected );
207
208 if ( !isEqualChecksum( expected, actual ) )
209 {
210 checksumPolicy.onChecksumMismatch(
211 factory.getName(), ChecksumKind.REMOTE_EXTERNAL,
212 new ChecksumFailureException( expected, ChecksumKind.REMOTE_EXTERNAL.name(), actual )
213 );
214 }
215 else if ( checksumPolicy.onChecksumMatch( factory.getName(), ChecksumKind.REMOTE_EXTERNAL ) )
216 {
217 return true;
218 }
219 }
220 catch ( IOException e )
221 {
222 checksumPolicy.onChecksumError(
223 factory.getName(), ChecksumKind.REMOTE_EXTERNAL, new ChecksumFailureException( e )
224 );
225 }
226 }
227 return false;
228 }
229
230 private static boolean isEqualChecksum( String expected, String actual )
231 {
232 return expected.equalsIgnoreCase( actual );
233 }
234
235 private File getChecksumFile( ChecksumAlgorithmFactory factory )
236 {
237 return new File( dataFile.getPath() + '.' + factory.getFileExtension() );
238 }
239
240 public void retry()
241 {
242 checksumPolicy.onTransferRetry();
243 checksumExpectedValues.clear();
244 }
245
246 public boolean handle( ChecksumFailureException exception )
247 {
248 return checksumPolicy.onTransferChecksumFailure( exception );
249 }
250
251 public void commit()
252 {
253 for ( Map.Entry<File, String> entry : checksumExpectedValues.entrySet() )
254 {
255 File checksumFile = entry.getKey();
256 try
257 {
258 fileProcessor.writeChecksum( checksumFile, entry.getValue() );
259 }
260 catch ( IOException e )
261 {
262 LOGGER.debug( "Failed to write checksum file {}", checksumFile, e );
263 throw new UncheckedIOException( e );
264 }
265 }
266 checksumExpectedValues.clear();
267 }
268 }