View Javadoc

1   package com.terradue.jcatalogue.client.download;
2   
3   import static java.nio.charset.Charset.forName;
4   import static java.lang.String.format;
5   import static java.security.MessageDigest.getInstance;
6   
7   import java.io.Closeable;
8   import java.io.File;
9   import java.io.FileNotFoundException;
10  import java.io.FileOutputStream;
11  import java.io.IOException;
12  import java.nio.ByteBuffer;
13  import java.nio.channels.FileChannel;
14  import java.nio.charset.Charset;
15  import java.security.MessageDigest;
16  import java.security.NoSuchAlgorithmException;
17  import java.util.List;
18  
19  import com.ning.http.client.AsyncHandler;
20  import com.ning.http.client.HttpResponseBodyPart;
21  import com.ning.http.client.HttpResponseHeaders;
22  import com.ning.http.client.HttpResponseStatus;
23  
24  /**
25   * @since 0.5
26   */
27  final class HttpDownloadHandler<T>
28      implements AsyncHandler<T>
29  {
30  
31      /**
32       * Used to build output as Hex
33       */
34      private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
35  
36      private static final Charset UTF_8 = forName( "UTF-8" );
37  
38      private static final String CONTENT_LENGTH = "Content-Length";
39  
40      private static final String MD5 = "MD5";
41  
42      private final File targetFile;
43  
44      private final FileOutputStream output;
45  
46      private final DownloadHandler<T> downloadHandler;
47  
48      private final MessageDigest md5Digest;
49  
50      private long contentLength = -1;
51  
52      private long downloadCounter = 0;
53  
54      public HttpDownloadHandler( File targetFile, DownloadHandler<T> downloadHandler )
55          throws FileNotFoundException
56      {
57          this.targetFile = targetFile;
58          output = new FileOutputStream( targetFile );
59          this.downloadHandler = downloadHandler;
60  
61          try
62          {
63              md5Digest = getInstance( MD5 );
64          }
65          catch ( NoSuchAlgorithmException e )
66          {
67              // should not happen
68              throw new RuntimeException( e.getMessage() );
69          }
70      }
71  
72      @Override
73      public void onThrowable( Throwable t )
74      {
75          downloadHandler.onError( t );
76      }
77  
78      @Override
79      public STATE onBodyPartReceived( HttpResponseBodyPart bodyPart )
80          throws Exception
81      {
82          bodyPart.writeTo( output );
83          md5Digest.update( bodyPart.getBodyPartBytes() );
84  
85          // print the percentage progress on shell
86          if ( contentLength > 0 )
87          {
88              downloadCounter += bodyPart.getBodyPartBytes().length;
89  
90              downloadHandler.onContentDownloadProgress( downloadCounter, contentLength );
91          }
92  
93          return STATE.CONTINUE;
94      }
95  
96      @Override
97      public STATE onStatusReceived( HttpResponseStatus responseStatus )
98          throws Exception
99      {
100         return STATE.CONTINUE;
101     }
102 
103     @Override
104     public STATE onHeadersReceived( HttpResponseHeaders headers )
105         throws Exception
106     {
107         List<String> contentLength = headers.getHeaders().get( CONTENT_LENGTH );
108 
109         if ( !contentLength.isEmpty() )
110         {
111             this.contentLength = Long.valueOf( contentLength.iterator().next() ).longValue();
112         }
113 
114         return STATE.CONTINUE;
115     }
116 
117     @Override
118     public T onCompleted()
119         throws Exception
120     {
121         output.flush();
122         closeQuietly( output );
123 
124         writeMd5ChecksumFile();
125 
126         return downloadHandler.onCompleted( targetFile );
127     }
128 
129     private void writeMd5ChecksumFile()
130     {
131         File checksumFile = new File( targetFile.getParent(), format( "%.md5", targetFile.getName() ) );
132         try
133         {
134             FileChannel digestChannel = new FileOutputStream( checksumFile ).getChannel();
135 
136             byte[] checksumData = md5Digest.digest();
137             char[] hexData = new char[checksumData.length << 1];
138 
139             for ( int i = 0, j = 0; i < checksumData.length; i++ )
140             {
141                 hexData[j++] = HEX_DIGITS[( 0xF0 & hexData[i] ) >>> 4];
142                 hexData[j++] = HEX_DIGITS[0x0F & hexData[i]];
143             }
144 
145             digestChannel.write( ByteBuffer.wrap( new String( hexData ).getBytes( UTF_8 ) ) );
146 
147             digestChannel.close();
148         }
149         catch ( Exception e )
150         {
151             // simply skip the .md5 file generation
152         }
153     }
154 
155     private static void closeQuietly( Closeable closeable )
156     {
157         if ( closeable != null )
158         {
159             try
160             {
161                 closeable.close();
162             }
163             catch ( IOException e )
164             {
165                 // close quietly
166             }
167         }
168     }
169 
170 }