View Javadoc
1   package org.eclipse.aether.connector.basic;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.nio.Buffer;
27  import java.nio.ByteBuffer;
28  import java.security.MessageDigest;
29  import java.security.NoSuchAlgorithmException;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Set;
37  
38  import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
39  import org.eclipse.aether.util.ChecksumUtils;
40  
41  /**
42   * Calculates checksums for a downloaded file.
43   */
44  final class ChecksumCalculator
45  {
46  
47      static class Checksum
48      {
49          final String algorithm;
50  
51          final MessageDigest digest;
52  
53          Exception error;
54  
55          Checksum( String algorithm )
56          {
57              this.algorithm = algorithm;
58              MessageDigest digest = null;
59              try
60              {
61                  digest = MessageDigest.getInstance( algorithm );
62              }
63              catch ( NoSuchAlgorithmException e )
64              {
65                  error = e;
66              }
67              this.digest = digest;
68          }
69  
70          public void update( ByteBuffer buffer )
71          {
72              if ( digest != null )
73              {
74                  digest.update( buffer );
75              }
76          }
77  
78          public void reset()
79          {
80              if ( digest != null )
81              {
82                  digest.reset();
83                  error = null;
84              }
85          }
86  
87          public void error( Exception error )
88          {
89              if ( digest != null )
90              {
91                  this.error = error;
92              }
93          }
94  
95          public Object get()
96          {
97              if ( error != null )
98              {
99                  return error;
100             }
101             return ChecksumUtils.toHexString( digest.digest() );
102         }
103 
104     }
105 
106     private final List<Checksum> checksums;
107 
108     private final File targetFile;
109 
110     public static ChecksumCalculator newInstance( File targetFile, Collection<RepositoryLayout.Checksum> checksums )
111     {
112         if ( checksums == null || checksums.isEmpty() )
113         {
114             return null;
115         }
116         return new ChecksumCalculator( targetFile, checksums );
117     }
118 
119     private ChecksumCalculator( File targetFile, Collection<RepositoryLayout.Checksum> checksums )
120     {
121         this.checksums = new ArrayList<>();
122         Set<String> algos = new HashSet<>();
123         for ( RepositoryLayout.Checksum checksum : checksums )
124         {
125             String algo = checksum.getAlgorithm();
126             if ( algos.add( algo ) )
127             {
128                 this.checksums.add( new Checksum( algo ) );
129             }
130         }
131         this.targetFile = targetFile;
132     }
133 
134     public void init( long dataOffset )
135     {
136         for ( Checksum checksum : checksums )
137         {
138             checksum.reset();
139         }
140         if ( dataOffset <= 0L )
141         {
142             return;
143         }
144 
145         InputStream in = null;
146         try
147         {
148             in = new FileInputStream( targetFile );
149             long total = 0;
150             ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
151             for ( byte[] array = buffer.array(); total < dataOffset; )
152             {
153                 int read = in.read( array );
154                 if ( read < 0 )
155                 {
156                     throw new IOException( targetFile + " contains only " + total
157                                                + " bytes, cannot resume download from offset " + dataOffset );
158                 }
159                 total += read;
160                 if ( total > dataOffset )
161                 {
162                     read -= total - dataOffset;
163                 }
164                 ( (Buffer) buffer ).rewind();
165                 ( (Buffer) buffer ).limit( read );
166                 update( buffer );
167             }
168 
169             in.close();
170             in = null;
171         }
172         catch ( IOException e )
173         {
174             for ( Checksum checksum : checksums )
175             {
176                 checksum.error( e );
177             }
178         }
179         finally
180         {
181             try
182             {
183                 if ( in != null )
184                 {
185                     in.close();
186                 }
187             }
188             catch ( IOException e )
189             {
190                 // Suppressed due to an exception already thrown in the try block.
191             }
192         }
193     }
194 
195     public void update( ByteBuffer data )
196     {
197         for ( Checksum checksum : checksums )
198         {
199             ( (Buffer) data ).mark();
200             checksum.update( data );
201             ( (Buffer) data ).reset();
202         }
203     }
204 
205     public Map<String, Object> get()
206     {
207         Map<String, Object> results = new HashMap<>();
208         for ( Checksum checksum : checksums )
209         {
210             results.put( checksum.algorithm, checksum.get() );
211         }
212         return results;
213     }
214 
215 }