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.connector.basic;
20  
21  import java.io.BufferedInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.nio.Buffer;
25  import java.nio.ByteBuffer;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Set;
35  
36  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm;
37  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
38  
39  import static java.util.Objects.requireNonNull;
40  
41  /**
42   * Calculates checksums for a downloaded file.
43   */
44  final class ChecksumCalculator {
45  
46      static class Checksum {
47          final ChecksumAlgorithmFactory checksumAlgorithmFactory;
48  
49          ChecksumAlgorithm algorithm;
50  
51          Exception error;
52  
53          Checksum(ChecksumAlgorithmFactory checksumAlgorithmFactory) {
54              this.checksumAlgorithmFactory = requireNonNull(checksumAlgorithmFactory);
55              this.algorithm = checksumAlgorithmFactory.getAlgorithm();
56          }
57  
58          public void reset() {
59              this.algorithm = checksumAlgorithmFactory.getAlgorithm();
60              this.error = null;
61          }
62  
63          public void update(ByteBuffer buffer) {
64              this.algorithm.update(buffer);
65          }
66  
67          public void error(Exception error) {
68              this.error = error;
69          }
70  
71          public Object get() {
72              if (error != null) {
73                  return error;
74              }
75              return algorithm.checksum();
76          }
77      }
78  
79      private final List<Checksum> checksums;
80  
81      private final Path targetFile;
82  
83      public static ChecksumCalculator newInstance(
84              Path targetFile, Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
85          if (checksumAlgorithmFactories == null || checksumAlgorithmFactories.isEmpty()) {
86              return null;
87          }
88          return new ChecksumCalculator(targetFile, checksumAlgorithmFactories);
89      }
90  
91      private ChecksumCalculator(Path targetFile, Collection<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
92          this.checksums = new ArrayList<>();
93          Set<String> algos = new HashSet<>();
94          for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) {
95              if (algos.add(checksumAlgorithmFactory.getName())) {
96                  this.checksums.add(new Checksum(checksumAlgorithmFactory));
97              }
98          }
99          this.targetFile = targetFile;
100     }
101 
102     public void init(long dataOffset) {
103         for (Checksum checksum : checksums) {
104             checksum.reset();
105         }
106         if (dataOffset <= 0L) {
107             return;
108         }
109 
110         try (InputStream in = new BufferedInputStream(Files.newInputStream(targetFile))) {
111             long total = 0;
112             final byte[] buffer = new byte[1024 * 32];
113             while (total < dataOffset) {
114                 int read = in.read(buffer);
115                 if (read < 0) {
116                     throw new IOException(targetFile + " contains only " + total
117                             + " bytes, cannot resume download from offset " + dataOffset);
118                 }
119                 total += read;
120                 if (total > dataOffset) {
121                     read -= total - dataOffset;
122                 }
123                 update(ByteBuffer.wrap(buffer, 0, read));
124             }
125         } catch (IOException e) {
126             for (Checksum checksum : checksums) {
127                 checksum.error(e);
128             }
129         }
130     }
131 
132     public void update(ByteBuffer data) {
133         for (Checksum checksum : checksums) {
134             ((Buffer) data).mark();
135             checksum.update(data);
136             ((Buffer) data).reset();
137         }
138     }
139 
140     public Map<String, Object> get() {
141         Map<String, Object> results = new HashMap<>();
142         for (Checksum checksum : checksums) {
143             results.put(checksum.checksumAlgorithmFactory.getName(), checksum.get());
144         }
145         return results;
146     }
147 }