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                     if ( total < dataOffset )
157                     {
158                         throw new IOException( targetFile + " contains only " + total
159                                                    + " bytes, cannot resume download from offset " + dataOffset );
160                     }
161                     break;
162                 }
163                 total += read;
164                 if ( total > dataOffset )
165                 {
166                     read -= total - dataOffset;
167                 }
168                 ( (Buffer) buffer ).rewind();
169                 ( (Buffer) buffer ).limit( read );
170                 update( buffer );
171             }
172 
173             in.close();
174             in = null;
175         }
176         catch ( IOException e )
177         {
178             for ( Checksum checksum : checksums )
179             {
180                 checksum.error( e );
181             }
182         }
183         finally
184         {
185             try
186             {
187                 if ( in != null )
188                 {
189                     in.close();
190                 }
191             }
192             catch ( IOException e )
193             {
194                 // Suppressed due to an exception already thrown in the try block.
195             }
196         }
197     }
198 
199     public void update( ByteBuffer data )
200     {
201         for ( Checksum checksum : checksums )
202         {
203             ( (Buffer) data ).mark();
204             checksum.update( data );
205             ( (Buffer) data ).reset();
206         }
207     }
208 
209     public Map<String, Object> get()
210     {
211         Map<String, Object> results = new HashMap<>();
212         for ( Checksum checksum : checksums )
213         {
214             results.put( checksum.algorithm, checksum.get() );
215         }
216         return results;
217     }
218 
219 }