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