| 1 | package net.oni2.aeinstaller.backend.network;
 | 
|---|
| 2 | 
 | 
|---|
| 3 | import java.io.BufferedInputStream;
 | 
|---|
| 4 | import java.io.File;
 | 
|---|
| 5 | import java.io.FileNotFoundException;
 | 
|---|
| 6 | import java.io.IOException;
 | 
|---|
| 7 | import java.io.RandomAccessFile;
 | 
|---|
| 8 | import java.net.URL;
 | 
|---|
| 9 | import java.net.URLConnection;
 | 
|---|
| 10 | import java.util.HashSet;
 | 
|---|
| 11 | 
 | 
|---|
| 12 | /**
 | 
|---|
| 13 |  * @author Christian Illy
 | 
|---|
| 14 |  */
 | 
|---|
| 15 | public class FileDownloader implements Runnable {
 | 
|---|
| 16 |         /**
 | 
|---|
| 17 |          * @author Christian Illy
 | 
|---|
| 18 |          */
 | 
|---|
| 19 |         public enum EState {
 | 
|---|
| 20 |                 /**
 | 
|---|
| 21 |                  * Downloader initialized but not started
 | 
|---|
| 22 |                  */
 | 
|---|
| 23 |                 INIT,
 | 
|---|
| 24 |                 /**
 | 
|---|
| 25 |                  * Download running
 | 
|---|
| 26 |                  */
 | 
|---|
| 27 |                 RUNNING,
 | 
|---|
| 28 |                 /**
 | 
|---|
| 29 |                  * Download suspended
 | 
|---|
| 30 |                  */
 | 
|---|
| 31 |                 PAUSED,
 | 
|---|
| 32 |                 /**
 | 
|---|
| 33 |                  * Download interrupted
 | 
|---|
| 34 |                  */
 | 
|---|
| 35 |                 INTERRUPTED,
 | 
|---|
| 36 |                 /**
 | 
|---|
| 37 |                  * Download finished successfully
 | 
|---|
| 38 |                  */
 | 
|---|
| 39 |                 FINISHED,
 | 
|---|
| 40 |                 /**
 | 
|---|
| 41 |                  * Aborted because of an error
 | 
|---|
| 42 |                  */
 | 
|---|
| 43 |                 ERROR
 | 
|---|
| 44 |         };
 | 
|---|
| 45 | 
 | 
|---|
| 46 |         private HashSet<FileDownloadListener> listeners = new HashSet<FileDownloadListener>();
 | 
|---|
| 47 |         private Thread t = null;
 | 
|---|
| 48 |         private URL url = null;
 | 
|---|
| 49 |         private File target = null;
 | 
|---|
| 50 |         private int size = -1;
 | 
|---|
| 51 |         private int downloaded = 0;
 | 
|---|
| 52 | 
 | 
|---|
| 53 |         private EState state = EState.INIT;
 | 
|---|
| 54 | 
 | 
|---|
| 55 |         /**
 | 
|---|
| 56 |          * @param url
 | 
|---|
| 57 |          *            URL of file to download
 | 
|---|
| 58 |          * @param target
 | 
|---|
| 59 |          *            Path of target file to save to
 | 
|---|
| 60 |          * @throws IOException
 | 
|---|
| 61 |          *             If url could not be opened
 | 
|---|
| 62 |          */
 | 
|---|
| 63 |         public FileDownloader(String url, String target) throws IOException {
 | 
|---|
| 64 |                 this.url = new URL(url);
 | 
|---|
| 65 |                 this.target = new File(target);
 | 
|---|
| 66 | 
 | 
|---|
| 67 |                 URLConnection connection = this.url.openConnection();
 | 
|---|
| 68 |                 connection.connect();
 | 
|---|
| 69 |                 size = connection.getContentLength();
 | 
|---|
| 70 |         }
 | 
|---|
| 71 | 
 | 
|---|
| 72 |         /**
 | 
|---|
| 73 |          * @param listener
 | 
|---|
| 74 |          *            Listener to add
 | 
|---|
| 75 |          */
 | 
|---|
| 76 |         public void addListener(FileDownloadListener listener) {
 | 
|---|
| 77 |                 listeners.add(listener);
 | 
|---|
| 78 |         }
 | 
|---|
| 79 | 
 | 
|---|
| 80 |         /**
 | 
|---|
| 81 |          * @param listener
 | 
|---|
| 82 |          *            Listener to remove
 | 
|---|
| 83 |          */
 | 
|---|
| 84 |         public void removeListener(FileDownloadListener listener) {
 | 
|---|
| 85 |                 listeners.remove(listener);
 | 
|---|
| 86 |         }
 | 
|---|
| 87 | 
 | 
|---|
| 88 |         /**
 | 
|---|
| 89 |          * Start the download process
 | 
|---|
| 90 |          */
 | 
|---|
| 91 |         public synchronized void start() {
 | 
|---|
| 92 |                 if (t == null) {
 | 
|---|
| 93 |                         t = new Thread(this);
 | 
|---|
| 94 |                         t.start();
 | 
|---|
| 95 |                         state = EState.RUNNING;
 | 
|---|
| 96 |                 }
 | 
|---|
| 97 |         }
 | 
|---|
| 98 | 
 | 
|---|
| 99 |         /**
 | 
|---|
| 100 |          * @param suspend
 | 
|---|
| 101 |          *            Suspend or resume
 | 
|---|
| 102 |          */
 | 
|---|
| 103 |         public synchronized void suspend(boolean suspend) {
 | 
|---|
| 104 |                 if ((state == EState.RUNNING) || (state == EState.PAUSED)) {
 | 
|---|
| 105 |                         if (suspend)
 | 
|---|
| 106 |                                 state = EState.PAUSED;
 | 
|---|
| 107 |                         else
 | 
|---|
| 108 |                                 state = EState.RUNNING;
 | 
|---|
| 109 |                 }
 | 
|---|
| 110 |         }
 | 
|---|
| 111 | 
 | 
|---|
| 112 |         /**
 | 
|---|
| 113 |          * Stop (abort) download
 | 
|---|
| 114 |          */
 | 
|---|
| 115 |         public synchronized void stop() {
 | 
|---|
| 116 |                 state = EState.INTERRUPTED;
 | 
|---|
| 117 |                 try {
 | 
|---|
| 118 |                         t.join();
 | 
|---|
| 119 |                 } catch (InterruptedException e) {
 | 
|---|
| 120 |                         // TODO Auto-generated catch block
 | 
|---|
| 121 |                         e.printStackTrace();
 | 
|---|
| 122 |                 }
 | 
|---|
| 123 |                 updateStatus(0, 1);
 | 
|---|
| 124 |                 t = null;
 | 
|---|
| 125 |                 if (target.exists())
 | 
|---|
| 126 |                         target.delete();
 | 
|---|
| 127 |         }
 | 
|---|
| 128 | 
 | 
|---|
| 129 |         private synchronized void updateStatus(int current, int total) {
 | 
|---|
| 130 |                 downloaded = current;
 | 
|---|
| 131 |                 for (FileDownloadListener l : listeners) {
 | 
|---|
| 132 |                         l.statusUpdate(this, state, current, total);
 | 
|---|
| 133 |                 }
 | 
|---|
| 134 |         }
 | 
|---|
| 135 | 
 | 
|---|
| 136 |         @Override
 | 
|---|
| 137 |         public void run() {
 | 
|---|
| 138 |                 int downloaded = 0;
 | 
|---|
| 139 |                 int fileLength = Integer.MAX_VALUE;
 | 
|---|
| 140 |                 String strLastModified = null;
 | 
|---|
| 141 |                 String strEtag = null;
 | 
|---|
| 142 |                 RandomAccessFile outFile = null;
 | 
|---|
| 143 |                 try {
 | 
|---|
| 144 |                         outFile = new RandomAccessFile(target, "rw");
 | 
|---|
| 145 |                 } catch (FileNotFoundException e1) {
 | 
|---|
| 146 |                         // TODO Auto-generated catch block
 | 
|---|
| 147 |                         e1.printStackTrace();
 | 
|---|
| 148 |                         state = EState.ERROR;
 | 
|---|
| 149 |                         return;
 | 
|---|
| 150 |                 }
 | 
|---|
| 151 | 
 | 
|---|
| 152 |                 while (downloaded < fileLength) {
 | 
|---|
| 153 |                         switch (state) {
 | 
|---|
| 154 |                                 case ERROR:
 | 
|---|
| 155 |                                         updateStatus(downloaded, fileLength);
 | 
|---|
| 156 |                                         return;
 | 
|---|
| 157 |                                 case PAUSED:
 | 
|---|
| 158 |                                         try {
 | 
|---|
| 159 |                                                 Thread.sleep(100);
 | 
|---|
| 160 |                                         } catch (InterruptedException e) {
 | 
|---|
| 161 |                                                 e.printStackTrace();
 | 
|---|
| 162 |                                         }
 | 
|---|
| 163 |                                         break;
 | 
|---|
| 164 |                                 case INTERRUPTED:
 | 
|---|
| 165 |                                         return;
 | 
|---|
| 166 |                                 case RUNNING:
 | 
|---|
| 167 |                                         BufferedInputStream input = null;
 | 
|---|
| 168 |                                         try {
 | 
|---|
| 169 |                                                 URLConnection connection = url.openConnection();
 | 
|---|
| 170 |                                                 if (downloaded == 0) {
 | 
|---|
| 171 |                                                         connection.connect();
 | 
|---|
| 172 |                                                         strLastModified = connection
 | 
|---|
| 173 |                                                                         .getHeaderField("Last-Modified");
 | 
|---|
| 174 |                                                         strEtag = connection.getHeaderField("ETag");
 | 
|---|
| 175 |                                                         fileLength = connection.getContentLength();
 | 
|---|
| 176 |                                                 } else {
 | 
|---|
| 177 |                                                         connection.setRequestProperty("Range", "bytes="
 | 
|---|
| 178 |                                                                         + downloaded + "-");
 | 
|---|
| 179 |                                                         if (strEtag != null)
 | 
|---|
| 180 |                                                                 connection.setRequestProperty("If-Range",
 | 
|---|
| 181 |                                                                                 strEtag);
 | 
|---|
| 182 |                                                         else
 | 
|---|
| 183 |                                                                 connection.setRequestProperty("If-Range",
 | 
|---|
| 184 |                                                                                 strLastModified);
 | 
|---|
| 185 |                                                         connection.connect();
 | 
|---|
| 186 |                                                 }
 | 
|---|
| 187 | 
 | 
|---|
| 188 |                                                 // Setup streams and buffers.
 | 
|---|
| 189 |                                                 input = new BufferedInputStream(
 | 
|---|
| 190 |                                                                 connection.getInputStream(), 8192);
 | 
|---|
| 191 |                                                 if (downloaded > 0)
 | 
|---|
| 192 |                                                         outFile.seek(downloaded);
 | 
|---|
| 193 |                                                 byte data[] = new byte[1024];
 | 
|---|
| 194 | 
 | 
|---|
| 195 |                                                 // Download file.
 | 
|---|
| 196 |                                                 int dataRead = 0;
 | 
|---|
| 197 |                                                 int i = 0;
 | 
|---|
| 198 |                                                 while (((dataRead = input.read(data, 0, 1024)) != -1)
 | 
|---|
| 199 |                                                                 && (state == EState.RUNNING)) {
 | 
|---|
| 200 |                                                         outFile.write(data, 0, dataRead);
 | 
|---|
| 201 |                                                         downloaded += dataRead;
 | 
|---|
| 202 |                                                         if (downloaded >= fileLength)
 | 
|---|
| 203 |                                                                 break;
 | 
|---|
| 204 | 
 | 
|---|
| 205 |                                                         i++;
 | 
|---|
| 206 |                                                         if ((i % 10) == 0)
 | 
|---|
| 207 |                                                                 updateStatus(downloaded, fileLength);
 | 
|---|
| 208 |                                                 }
 | 
|---|
| 209 |                                                 input.close();
 | 
|---|
| 210 |                                         } catch (IOException e) {
 | 
|---|
| 211 |                                                 // TODO Auto-generated catch block
 | 
|---|
| 212 |                                                 e.printStackTrace();
 | 
|---|
| 213 |                                                 try {
 | 
|---|
| 214 |                                                         if (input != null)
 | 
|---|
| 215 |                                                                 input.close();
 | 
|---|
| 216 |                                                 } catch (IOException e2) {
 | 
|---|
| 217 |                                                         e2.printStackTrace();
 | 
|---|
| 218 |                                                 }
 | 
|---|
| 219 |                                         }
 | 
|---|
| 220 |                                         break;
 | 
|---|
| 221 |                                 default:
 | 
|---|
| 222 |                                         break;
 | 
|---|
| 223 |                         }
 | 
|---|
| 224 |                 }
 | 
|---|
| 225 | 
 | 
|---|
| 226 |                 try {
 | 
|---|
| 227 |                         // Close streams.
 | 
|---|
| 228 |                         outFile.close();
 | 
|---|
| 229 |                 } catch (IOException e) {
 | 
|---|
| 230 |                         e.printStackTrace();
 | 
|---|
| 231 |                 }
 | 
|---|
| 232 | 
 | 
|---|
| 233 |                 state = EState.FINISHED;
 | 
|---|
| 234 |                 updateStatus(downloaded, fileLength);
 | 
|---|
| 235 |         }
 | 
|---|
| 236 | 
 | 
|---|
| 237 |         /**
 | 
|---|
| 238 |          * @return the target
 | 
|---|
| 239 |          */
 | 
|---|
| 240 |         public File getTarget() {
 | 
|---|
| 241 |                 return target;
 | 
|---|
| 242 |         }
 | 
|---|
| 243 | 
 | 
|---|
| 244 |         /**
 | 
|---|
| 245 |          * @return the size
 | 
|---|
| 246 |          */
 | 
|---|
| 247 |         public int getSize() {
 | 
|---|
| 248 |                 return size;
 | 
|---|
| 249 |         }
 | 
|---|
| 250 | 
 | 
|---|
| 251 |         /**
 | 
|---|
| 252 |          * @return the downloaded size
 | 
|---|
| 253 |          */
 | 
|---|
| 254 |         public int getDownloaded() {
 | 
|---|
| 255 |                 return downloaded;
 | 
|---|
| 256 |         }
 | 
|---|
| 257 | 
 | 
|---|
| 258 | }
 | 
|---|