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.ByteBuffer;
27  import java.security.MessageDigest;
28  import java.security.NoSuchAlgorithmException;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
38  import org.eclipse.aether.util.ChecksumUtils;
39  
40  /**
41   * Calculates checksums for a downloaded file.
42   */
43  final class ChecksumCalculator
44  {
45  
46      static class Checksum
47      {
48          final String algorithm;
49  
50          final MessageDigest digest;
51  
52          Exception error;
53  
54          public Checksum( String algorithm )
55          {
56              this.algorithm = algorithm;
57              MessageDigest digest = null;
58              try
59              {
60                  digest = MessageDigest.getInstance( algorithm );
61              }
62              catch ( NoSuchAlgorithmException e )
63              {
64                  error = e;
65              }
66              this.digest = digest;
67          }
68  
69          public void update( ByteBuffer buffer )
70          {
71              if ( digest != null )
72              {
73                  digest.update( buffer );
74              }
75          }
76  
77          public void reset()
78          {
79              if ( digest != null )
80              {
81                  digest.reset();
82                  error = null;
83              }
84          }
85  
86          public void error( Exception error )
87          {
88              if ( digest != null )
89              {
90                  this.error = error;
91              }
92          }
93  
94          public Object get()
95          {
96              if ( error != null )
97              {
98                  return error;
99              }
100             return ChecksumUtils.toHexString( digest.digest() );
101         }
102 
103     }
104 
105     private final List<Checksum> checksums;
106 
107     private final File targetFile;
108 
109     public static ChecksumCalculator newInstance( File targetFile, Collection<RepositoryLayout.Checksum> checksums )
110     {
111         if ( checksums == null || checksums.isEmpty() )
112         {
113             return null;
114         }
115         return new ChecksumCalculator( targetFile, checksums );
116     }
117 
118     private ChecksumCalculator( File targetFile, Collection<RepositoryLayout.Checksum> checksums )
119     {
120         this.checksums = new ArrayList<Checksum>();
121         Set<String> algos = new HashSet<String>();
122         for ( RepositoryLayout.Checksum checksum : checksums )
123         {
124             String algo = checksum.getAlgorithm();
125             if ( algos.add( algo ) )
126             {
127                 this.checksums.add( new Checksum( algo ) );
128             }
129         }
130         this.targetFile = targetFile;
131     }
132 
133     public void init( long dataOffset )
134     {
135         for ( Checksum checksum : checksums )
136         {
137             checksum.reset();
138         }
139         if ( dataOffset <= 0L )
140         {
141             return;
142         }
143 
144         InputStream in = null;
145         try
146         {
147             in = new FileInputStream( targetFile );
148             long total = 0;
149             ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
150             for ( byte[] array = buffer.array(); total < dataOffset; )
151             {
152                 int read = in.read( array );
153                 if ( read < 0 )
154                 {
155                     if ( total < dataOffset )
156                     {
157                         throw new IOException( targetFile + " contains only " + total
158                                                    + " bytes, cannot resume download from offset " + dataOffset );
159                     }
160                     break;
161                 }
162                 total += read;
163                 if ( total > dataOffset )
164                 {
165                     read -= total - dataOffset;
166                 }
167                 buffer.rewind();
168                 buffer.limit( read );
169                 update( buffer );
170             }
171 
172             in.close();
173             in = null;
174         }
175         catch ( IOException e )
176         {
177             for ( Checksum checksum : checksums )
178             {
179                 checksum.error( e );
180             }
181         }
182         finally
183         {
184             try
185             {
186                 if ( in != null )
187                 {
188                     in.close();
189                 }
190             }
191             catch ( IOException e )
192             {
193                 // Suppressed due to an exception already thrown in the try block.
194             }
195         }
196     }
197 
198     public void update( ByteBuffer data )
199     {
200         for ( Checksum checksum : checksums )
201         {
202             data.mark();
203             checksum.update( data );
204             data.reset();
205         }
206     }
207 
208     public Map<String, Object> get()
209     {
210         Map<String, Object> results = new HashMap<String, Object>();
211         for ( Checksum checksum : checksums )
212         {
213             results.put( checksum.algorithm, checksum.get() );
214         }
215         return results;
216     }
217 
218 }