1: /**
2: * Download Manager
3: *
6: *
7: * Save-Listing1 as>Download.java, Listing 2 as>DownloadManager.java,
8: Listing 3 as>DownloadsTableModel.java,Listing 4
9: as>ProgressRenderer.java.
10: Then Compile like>javac DownloadManager.java
11: DownloadsTableModel.java
12: ProgressRenderer.java Download.java & then
13: > javaw DownloadManager
14: */
15: listing 1
16: import java.io.*;
17: import java.net.*;
18: import java.util.*;
19:
20: // This class downloads a file from a URL.
21: class Download extends Observable implements Runnable {
22: // Max size of download buffer.
23: private static final int MAX_BUFFER_SIZE = 1024;
24:
25: // These are the status names.
26: public static final String STATUSES[] = {"Downloading",
27: "Paused", "Complete", "Cancelled", "Error"};
28:
29: // These are the status codes.
30: public static final int DOWNLOADING = 0;
31: public static final int PAUSED = 1;
32: public static final int COMPLETE = 2;
33: public static final int CANCELLED = 3;
34: public static final int ERROR = 4;
35:
36: private URL url; // download URL
37: private int size; // size of download in bytes
38: private int downloaded; // number of bytes downloaded
39: private int status; // current status of download
40:
41: // Constructor for Download.
42: public Download(URL url) {
43: this.url = url;
44: size = -1;
45: downloaded = 0;
46: status = DOWNLOADING;
47:
48: // Begin the download.
49: download();
50: }
51:
52: // Get this download's URL.
53: public String getUrl() {
54: return url.toString();
55: }
56:
57: // Get this download's size.
58: public int getSize() {
59: return size;
60: }
61:
62: // Get this download's progress.
63: public float getProgress() {
64: return ((float) downloaded / size) * 100;
65: }
66:
67: // Get this download's status.
68: public int getStatus() {
69: return status;
70: }
71:
72: // Pause this download.
73: public void pause() {
74: status = PAUSED;
75: stateChanged();
76: }
77:
78: // Resume this download.
79: public void resume() {
80: status = DOWNLOADING;
81: stateChanged();
82: download();
83: }
84:
85: // Cancel this download.
86: public void cancel() {
87: status = CANCELLED;
88: stateChanged();
89: }
90:
91: // Mark this download as having an error.
92: private void error() {
93: status = ERROR;
94: stateChanged();
95: }
96:
97: // Start or resume downloading.
98: private void download() {
99: Thread thread = new Thread(this);
100: thread.start();
101: }
102:
103: // Get file name portion of URL.
104: private String getFileName(URL url) {
105: String fileName = url.getFile();
106: return fileName.substring(fileName.lastIndexOf('/') + 1);
107: }
108:
109: // Download file.
110: public void run() {
111: RandomAccessFile file = null;
112: InputStream stream = null;
113:
114: try {
115: // Open connection to URL.
116: HttpURLConnection connection =
117: (HttpURLConnection) url.openConnection();
118:
119: // Specify what portion of file to download.
120: connection.setRequestProperty("Range",
121: "bytes=" + downloaded + "-");
122:
123: // Connect to server.
124: connection.connect();
125:
126: // Make sure response code is in the 200 range.
127: if (connection.getResponseCode() / 100 != 2) {
128: error();
129: }
130:
131: // Check for valid content length.
132: int contentLength = connection.getContentLength();
133: if (contentLength < 1) {
134: error();
135: }
136:
137: /* Set the size for this download if it
138: hasn't been already set. */
139: if (size == -1) {
140: size = contentLength;
141: stateChanged();
142: }
143:
144: // Open file and seek to the end of it.
145: file = new RandomAccessFile(getFileName(url), "rw");
146: file.seek(downloaded);
147:
148: stream = connection.getInputStream();
149: while (status == DOWNLOADING) {
150: /* Size buffer according to how much of the
151: file is left to download. */
152: byte buffer[];
153: if (size - downloaded > MAX_BUFFER_SIZE) {
154: buffer = new byte[MAX_BUFFER_SIZE];
155: } else {
156: buffer = new byte[size - downloaded];
157: }
158:
159: // Read from server into buffer.
160: int read = stream.read(buffer);
161: if (read == -1)
162: break;
163:
164: // Write buffer to file.
165: file.write(buffer, 0, read);
166: downloaded += read;
167: stateChanged();
168: }
169:
170: /* Change status to complete if this point was
171: reached because downloading has finished. */
172: if (status == DOWNLOADING) {
173: status = COMPLETE;
174: stateChanged();
175: }
176: } catch (Exception e) {
177: error();
178: } finally {
179: // Close file.
180: if (file != null) {
181: try {
182: file.close();
183: } catch (Exception e) {}
184: }
185:
186: // Close connection to server.
187: if (stream != null) {
188: try {
189: stream.close();
190: } catch (Exception e) {}
191: }
192: }
193: }
194:
195: // Notify observers that this download's status has changed.
196: private void stateChanged() {
197: setChanged();
198: notifyObservers();
199: }
200: }
201:
202: listing 2
203: import java.awt.*;
204: import java.awt.event.*;
205: import java.net.*;
206: import java.util.*;
207: import javax.swing.*;
208: import javax.swing.event.*;
209:
210: // The Download Manager.
211: public class DownloadManager extends JFrame
212: implements Observer
213: {
214: // Add download text field.
215: private JTextField addTextField;
216:
217: // Download table's data model.
218: private DownloadsTableModel tableModel;
219:
220: // Table showing downloads.
221: private JTable table;
222:
223: // These are the buttons for managing the selected download.
224: private JButton pauseButton, resumeButton;
225: private JButton cancelButton, clearButton;
226:
227: // Currently selected download.
228: private Download selectedDownload;
229:
230: // Flag for whether or not table selection is being cleared.
231: private boolean clearing;
232:
233: // Constructor for Download Manager.
234: public DownloadManager()
235: {
236: // Set application title.
237: setTitle("Download Manager");
238:
239: // Set window size.
240: setSize(640, 480);
241:
242: // Handle window closing events.
243: addWindowListener(new WindowAdapter() {
244: public void windowClosing(WindowEvent e) {
245: actionExit();
246: }
247: });
248:
249: // Set up file menu.
250: JMenuBar menuBar = new JMenuBar();
251: JMenu fileMenu = new JMenu("File");
252: fileMenu.setMnemonic(KeyEvent.VK_F);
253: JMenuItem fileExitMenuItem = new JMenuItem("Exit",
254: KeyEvent.VK_X);
255: fileExitMenuItem.addActionListener(new ActionListener() {
256: public void actionPerformed(ActionEvent e) {
257: actionExit();
258: }
259: });
260: fileMenu.add(fileExitMenuItem);
261: menuBar.add(fileMenu);
262: setJMenuBar(menuBar);
263:
264: // Set up add panel.
265: JPanel addPanel = new JPanel();
266: addTextField = new JTextField(30);
267: addPanel.add(addTextField);
268: JButton addButton = new JButton("Add Download");
269: addButton.addActionListener(new ActionListener() {
270: public void actionPerformed(ActionEvent e) {
271: actionAdd();
272: }
273: });
274: addPanel.add(addButton);
275:
276: // Set up Downloads table.
277: tableModel = new DownloadsTableModel();
278: table = new JTable(tableModel);
279: table.getSelectionModel().addListSelectionListener(new
280: ListSelectionListener() {
281: public void valueChanged(ListSelectionEvent e) {
282: tableSelectionChanged();
283: }
284: });
285: // Allow only one row at a time to be selected.
286: table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
287:
288: // Set up ProgressBar as renderer for progress column.
289: ProgressRenderer renderer = new ProgressRenderer(0, 100);
290: renderer.setStringPainted(true); // show progress text
291: table.setDefaultRenderer(JProgressBar.class, renderer);
292:
293: // Set table's row height large enough to fit JProgressBar.
294: table.setRowHeight(
295: (int) renderer.getPreferredSize().getHeight());
296:
297: // Set up downloads panel.
298: JPanel downloadsPanel = new JPanel();
299: downloadsPanel.setBorder(
300: BorderFactory.createTitledBorder("Downloads"));
301: downloadsPanel.setLayout(new BorderLayout());
302: downloadsPanel.add(new JScrollPane(table),
303: BorderLayout.CENTER);
304:
305: // Set up buttons panel.
306: JPanel buttonsPanel = new JPanel();
307: pauseButton = new JButton("Pause");
308: pauseButton.addActionListener(new ActionListener() {
309: public void actionPerformed(ActionEvent e) {
310: actionPause();
311: }
312: });
313: pauseButton.setEnabled(false);
314: buttonsPanel.add(pauseButton);
315: resumeButton = new JButton("Resume");
316: resumeButton.addActionListener(new ActionListener() {
317: public void actionPerformed(ActionEvent e) {
318: actionResume();
319: }
320: });
321: resumeButton.setEnabled(false);
322: buttonsPanel.add(resumeButton);
323: cancelButton = new JButton("Cancel");
324: cancelButton.addActionListener(new ActionListener() {
325: public void actionPerformed(ActionEvent e) {
326: actionCancel();
327: }
328: });
329: cancelButton.setEnabled(false);
330: buttonsPanel.add(cancelButton);
331: clearButton = new JButton("Clear");
332: clearButton.addActionListener(new ActionListener() {
333: public void actionPerformed(ActionEvent e) {
334: actionClear();
335: }
336: });
337: clearButton.setEnabled(false);
338: buttonsPanel.add(clearButton);
339:
340: // Add panels to display.
341: getContentPane().setLayout(new BorderLayout());
342: getContentPane().add(addPanel, BorderLayout.NORTH);
343: getContentPane().add(downloadsPanel, BorderLayout.CENTER);
344: getContentPane().add(buttonsPanel, BorderLayout.SOUTH);
345: }
346:
347: // Exit this program.
348: private void actionExit() {
349: System.exit(0);
350: }
351:
352: // Add a new download.
353: private void actionAdd() {
354: URL verifiedUrl = verifyUrl(addTextField.getText());
355: if (verifiedUrl != null) {
356: tableModel.addDownload(new Download(verifiedUrl));
357: addTextField.setText(""); // reset add text field
358: } else {
359: JOptionPane.showMessageDialog(this,
360: "Invalid Download URL", "Error",
361: JOptionPane.ERROR_MESSAGE);
362: }
363: }
364:
365: // Verify download URL.
366: private URL verifyUrl(String url) {
367: // Only allow HTTP URLs.
368: if (!url.toLowerCase().startsWith("http://"))
369: return null;
370:
371: // Verify format of URL.
372: URL verifiedUrl = null;
373: try {
374: verifiedUrl = new URL(url);
375: } catch (Exception e) {
376: return null;
377: }
378:
379: // Make sure URL specifies a file.
380: if (verifiedUrl.getFile().length() < 2)
381: return null;
382:
383: return verifiedUrl;
384: }
385:
386: // Called when table row selection changes.
387: private void tableSelectionChanged() {
388: /* Unregister from receiving notifications
389: from the last selected download. */
390: if (selectedDownload != null)
391: selectedDownload.deleteObserver(DownloadManager.this);
392:
393: /* If not in the middle of clearing a download,
394: set the selected download and register to
395: receive notifications from it. */
396: if (!clearing && table.getSelectedRow() > -1) {
397: selectedDownload =
398: tableModel.getDownload(table.getSelectedRow());
399: selectedDownload.addObserver(DownloadManager.this);
400: updateButtons();
401: }
402: }
403:
404: // Pause the selected download.
405: private void actionPause() {
406: selectedDownload.pause();
407: updateButtons();
408: }
409:
410: // Resume the selected download.
411: private void actionResume() {
412: selectedDownload.resume();
413: updateButtons();
414: }
415:
416: // Cancel the selected download.
417: private void actionCancel() {
418: selectedDownload.cancel();
419: updateButtons();
420: }
421:
422: // Clear the selected download.
423: private void actionClear() {
424: clearing = true;
425: tableModel.clearDownload(table.getSelectedRow());
426: clearing = false;
427: selectedDownload = null;
428: updateButtons();
429: }
430:
431: /* Update each button's state based off of the
432: currently selected download's status. */
433: private void updateButtons() {
434: if (selectedDownload != null) {
435: int status = selectedDownload.getStatus();
436: switch (status) {
437: case Download.DOWNLOADING:
438: pauseButton.setEnabled(true);
439: resumeButton.setEnabled(false);
440: cancelButton.setEnabled(true);
441: clearButton.setEnabled(false);
442: break;
443: case Download.PAUSED:
444: pauseButton.setEnabled(false);
445: resumeButton.setEnabled(true);
446: cancelButton.setEnabled(true);
447: clearButton.setEnabled(false);
448: break;
449: case Download.ERROR:
450: pauseButton.setEnabled(false);
451: resumeButton.setEnabled(true);
452: cancelButton.setEnabled(false);
453: clearButton.setEnabled(true);
454: break;
455: default: // COMPLETE or CANCELLED
456: pauseButton.setEnabled(false);
457: resumeButton.setEnabled(false);
458: cancelButton.setEnabled(false);
459: clearButton.setEnabled(true);
460: }
461: } else {
462: // No download is selected in table.
463: pauseButton.setEnabled(false);
464: resumeButton.setEnabled(false);
465: cancelButton.setEnabled(false);
466: clearButton.setEnabled(false);
467: }
468: }
469:
470: /* Update is called when a Download notifies its
471: observers of any changes. */
472: public void update(Observable o, Object arg) {
473: // Update buttons if the selected download has changed.
474: if (selectedDownload != null && selectedDownload.equals(o))
475: updateButtons();
476: }
477:
478: // Run the Download Manager.
479: public static void main(String[] args) {
480: DownloadManager manager = new DownloadManager();
481: manager.setVisible(true);
482: }
483: }
484:
485: listing 3
486: import java.util.*;
487: import javax.swing.*;
488: import javax.swing.table.*;
489:
490: // This class manages the download table's data.
491: class DownloadsTableModel extends AbstractTableModel
492: implements Observer
493: {
494: // These are the names for the table's columns.
495: private static final String[] columnNames = {"URL", "Size",
496: "Progress", "Status"};
497:
498: // These are the classes for each column's values.
499: private static final Class[] columnClasses = {String.class,
500: String.class, JProgressBar.class, String.class};
501:
502: // The table's list of downloads.
503: private ArrayList<Download> downloadList =
504: new ArrayList<Download>();
505:
506: // Add a new download to the table.
507: public void addDownload(Download download) {
508: // Register to be notified when the download changes.
509: download.addObserver(this);
510:
511: downloadList.add(download);
512:
513: // Fire table row insertion notification to table.
514: fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
515: }
516:
517: // Get a download for the specified row.
518: public Download getDownload(int row) {
519: return (Download) downloadList.get(row);
520: }
521:
522: // Remove a download from the list.
523: public void clearDownload(int row) {
524: downloadList.remove(row);
525:
526: // Fire table row deletion notification to table.
527: fireTableRowsDeleted(row, row);
528: }
529:
530: // Get table's column count.
531: public int getColumnCount() {
532: return columnNames.length;
533: }
534:
535: // Get a column's name.
536: public String getColumnName(int col) {
537: return columnNames[col];
538: }
539:
540: // Get a column's class.
541: public Class getColumnClass(int col) {
542: return columnClasses[col];
543: }
544:
545: // Get table's row count.
546: public int getRowCount() {
547: return downloadList.size();
548: }
549:
550: // Get value for a specific row and column combination.
551: public Object getValueAt(int row, int col) {
552: Download download = downloadList.get(row);
553: switch (col) {
554: case 0: // URL
555: return download.getUrl();
556: case 1: // Size
557: int size = download.getSize();
558: return (size == -1) ? "" : Integer.toString(size);
559: case 2: // Progress
560: return new Float(download.getProgress());
561: case 3: // Status
562: return Download.STATUSES[download.getStatus()];
563: }
564: return "";
565: }
566:
567: /* Update is called when a Download notifies its
568: observers of any changes */
569: public void update(Observable o, Object arg) {
570: int index = downloadList.indexOf(o);
571:
572: // Fire table row update notification to table.
573: fireTableRowsUpdated(index, index);
574: }
575: }
576:
577: listing 4
578: import java.awt.*;
579: import javax.swing.*;
580: import javax.swing.table.*;
581:
582: // This class renders a JProgressBar in a table cell.
583: class ProgressRenderer extends JProgressBar
584: implements TableCellRenderer
585: {
586: // Constructor for ProgressRenderer.
587: public ProgressRenderer(int min, int max) {
588: super(min, max);
589: }
590:
591: /* Returns this JProgressBar as the renderer
592: for the given table cell. */
593: public Component getTableCellRendererComponent(
594: JTable table, Object value, boolean isSelected,
595: boolean hasFocus, int row, int column)
596: {
597: // Set JProgressBar's percent complete value.
598: setValue((int) ((Float) value).floatValue());
599: return this;
600: }
601: }
Post a Comment