001 /*
002 *
003 * $Revision: 13085 $ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06 Feb 2008) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.frontend.fileupload;
025
026 import java.awt.Dimension;
027 import java.awt.Frame;
028 import java.awt.GridBagConstraints;
029 import java.awt.GridBagLayout;
030 import java.awt.Insets;
031 import java.awt.Toolkit;
032 import java.awt.event.ActionEvent;
033 import java.awt.event.ActionListener;
034 import java.awt.event.WindowAdapter;
035 import java.awt.event.WindowEvent;
036 import java.io.File;
037 import java.io.FileInputStream;
038 import java.text.DecimalFormat;
039
040 import javax.swing.BorderFactory;
041 import javax.swing.JButton;
042 import javax.swing.JDialog;
043 import javax.swing.JLabel;
044 import javax.swing.JOptionPane;
045 import javax.swing.JPanel;
046 import javax.swing.JProgressBar;
047 import javax.swing.SwingUtilities;
048 import javax.swing.UIManager;
049 import javax.swing.WindowConstants;
050
051 /**
052 * This class implements a Dialog that shows messages and a progress bar while
053 * creating, updating or deleting the derivates of a document. This class is a
054 * singleton, there is only one instance at a time that you get with
055 * getDialog(). The MCRUploadProgressMonitor provides methods to set the next
056 * message that should be displayed and to start, update and finish the progress
057 * bar.
058 *
059 * @author Frank Lützenkirchen
060 * @author Jens Kupferschmidt
061 * @version $Revision: 13085 $ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06 Feb 2008) $
062 */
063 public class MCRUploadProgressMonitor extends JDialog {
064 protected MCRUploadApplet applet;
065
066 protected boolean canceled; // if true, upload is canceled
067
068 protected boolean finished; // if true, upload process is finished
069
070 protected String filename;
071
072 protected JLabel lbFilename; // Current filename without path
073
074 protected long sizeFile; // Size of file currently uploaded
075
076 protected long bytesFile; // Number of bytes uploaded for current file
077
078 protected String fileTrans;
079
080 protected JLabel lbBytesFile; // Bytes current / total for this file
081
082 protected int fileProg;
083
084 protected JProgressBar pbFile; // Progress bar for current file
085
086 protected int numFiles; // Total number of files uploading
087
088 protected int fileCount; // Number of file currently uploading
089
090 protected JLabel lbNumFiles; // Number of files transferred / total
091
092 protected long sizeTotal; // Total size of all files uploading
093
094 protected long bytesTotal; // Total number of bytes uploaded so far
095
096 protected JLabel lbBytesTotal; // Bytes transferred / total of all files
097
098 protected JProgressBar pbTotal; // Progress bar for total upload
099
100 protected long startTime; // Time the upload startet
101
102 protected long endTime; // Time the upload finished
103
104 protected long lastUpdate; // Time the last data update was drawn
105
106 protected JLabel lbThroughput; // Average number of bytes per second
107
108 protected JLabel lbTime; // Time elapsed / estimated time remaining
109
110 protected JButton button; // OK / Cancel button to close window
111
112 /**
113 * Creates a new upload progress monitor and makes it visible.
114 *
115 * @param numFiles
116 * total number of files to be uploaded
117 * @param sizeTotal
118 * total byte size of all files
119 * @param applet
120 * the UploadApplet this monitor belongs to
121 */
122 protected MCRUploadProgressMonitor(int numFiles, long sizeTotal, MCRUploadApplet applet) {
123 super((Frame) null, "Dateien werden hochgeladen", true);
124
125 int width = 600;
126 int height = 300;
127
128 this.applet = applet;
129 this.canceled = false;
130 this.finished = false;
131 this.filename = "";
132 this.lbFilename = new JLabel(" ");
133 this.sizeFile = 0;
134 this.bytesFile = 0;
135 this.lbBytesFile = new JLabel(" ");
136 this.pbFile = new JProgressBar(0, 1000);
137 this.numFiles = numFiles;
138 this.fileCount = 0;
139 this.lbNumFiles = new JLabel(" ");
140 this.sizeTotal = sizeTotal;
141 this.bytesTotal = 0;
142 this.lbBytesTotal = new JLabel(" ");
143 this.pbTotal = new JProgressBar(0, 1000);
144 this.startTime = System.currentTimeMillis();
145 this.lbThroughput = new JLabel(" ");
146 this.lbTime = new JLabel(" ");
147 this.button = new JButton("Abbrechen");
148
149 button.setEnabled(true);
150 button.addActionListener(new ActionListener() {
151 public void actionPerformed(ActionEvent evt) {
152 if (finished)
153 MCRUploadProgressMonitor.this.close();
154 else
155 MCRUploadProgressMonitor.this.cancel();
156 }
157 });
158
159 pbFile.setStringPainted(true);
160 pbTotal.setStringPainted(true);
161
162 try {
163 UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
164 } catch (Exception ignored) {
165 }
166
167 JPanel content = new JPanel();
168 content.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
169 setContentPane(content);
170
171 GridBagLayout gbl = new GridBagLayout();
172 GridBagConstraints gbc = new GridBagConstraints();
173 content.setLayout(gbl);
174
175 gbc.anchor = GridBagConstraints.CENTER;
176 gbc.fill = GridBagConstraints.HORIZONTAL;
177 gbc.gridx = 0;
178 gbc.gridy = 0;
179 gbc.insets = new Insets(0, (width / 2) - 10, 0, (width / 2) - 10);
180
181 JLabel dummy = new JLabel("");
182 gbl.setConstraints(dummy, gbc);
183 content.add(dummy);
184
185 gbc.anchor = GridBagConstraints.WEST;
186 gbc.fill = GridBagConstraints.NONE;
187 gbc.insets = new Insets(2, 0, 2, 0);
188
189 gbc.gridy = 1;
190 gbl.setConstraints(lbNumFiles, gbc);
191 content.add(lbNumFiles);
192
193 gbc.gridy = 2;
194 gbl.setConstraints(lbBytesTotal, gbc);
195 content.add(lbBytesTotal);
196
197 gbc.gridy = 4;
198 gbl.setConstraints(lbFilename, gbc);
199 content.add(lbFilename);
200
201 gbc.gridy = 5;
202 gbl.setConstraints(lbBytesFile, gbc);
203 content.add(lbBytesFile);
204
205 gbc.gridy = 7;
206 gbl.setConstraints(lbThroughput, gbc);
207 content.add(lbThroughput);
208
209 gbc.gridy = 8;
210 gbl.setConstraints(lbTime, gbc);
211 content.add(lbTime);
212
213 gbc.anchor = GridBagConstraints.CENTER;
214 gbc.fill = GridBagConstraints.HORIZONTAL;
215 gbc.insets = new Insets(2, 0, 20, 0);
216
217 gbc.gridy = 3;
218 gbl.setConstraints(pbTotal, gbc);
219 content.add(pbTotal);
220
221 gbc.gridy = 6;
222 gbl.setConstraints(pbFile, gbc);
223 content.add(pbFile);
224
225 gbc.anchor = GridBagConstraints.EAST;
226 gbc.fill = GridBagConstraints.NONE;
227 gbc.insets = new Insets(20, 0, 0, 0);
228
229 gbc.gridy = 9;
230 gbl.setConstraints(button, gbc);
231 content.add(button);
232
233 setSize(width, height);
234
235 Dimension size = this.getSize();
236 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
237 setLocation((screen.width - size.width) / 2, (screen.height - size.height) / 2);
238
239 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
240
241 Runnable starter = new Runnable() {
242 public void run() {
243 MCRUploadProgressMonitor.this.setVisible(true);
244 MCRUploadProgressMonitor.this.requestFocus();
245 }
246 };
247
248 // This thread will update display even if upload gets very slow
249 Thread updater = new Thread(new Runnable() {
250 public void run() {
251
252 while (MCRUploadProgressMonitor.this.lastUpdate == 0) {
253 try {
254 Thread.sleep(1500);
255 } catch (InterruptedException ex) {
256 }
257 }
258
259 while (!(MCRUploadProgressMonitor.this.finished || MCRUploadProgressMonitor.this.canceled)) {
260 try {
261 Thread.sleep(500);
262 } catch (InterruptedException ex) {
263 }
264
265 if (MCRUploadProgressMonitor.this.finished || MCRUploadProgressMonitor.this.canceled)
266 return;
267
268 long now = System.currentTimeMillis();
269 if (now - MCRUploadProgressMonitor.this.lastUpdate > 900)
270 MCRUploadProgressMonitor.this.update();
271 }
272 }
273 });
274
275 SwingUtilities.invokeLater(starter);
276 updater.start();
277 }
278
279 protected void cancel() {
280 canceled = true;
281 endTime = System.currentTimeMillis();
282 end();
283 }
284
285 protected void close() {
286 button.setEnabled(false);
287 setVisible(false);
288 dispose();
289
290 if (applet != null)
291 applet.returnToURL();
292 }
293
294 protected DecimalFormat df = new DecimalFormat("00");
295
296 protected String formatTime(int sec) {
297 if (sec < 60) {
298 return sec + " Sek.";
299 }
300
301 int min = sec / 60;
302 sec = sec - (min * 60);
303
304 if (min < 60) {
305 return min + ":" + df.format(sec) + " Min.";
306 }
307
308 int hh = min / 60;
309 min = min - (hh * 60);
310
311 return hh + ":" + df.format(min) + ":" + df.format(sec) + " Std.";
312 }
313
314 protected String formatSize(long bytes) {
315 if (bytes < 1024) {
316 return bytes + " Byte";
317 }
318
319 long kb = bytes / 1024L;
320 bytes = bytes - (kb * 1024L);
321
322 long nk = Math.round((bytes / 1024d) * 100d);
323
324 if (kb < 1024) {
325 return kb + "," + df.format(nk) + " KB";
326 }
327
328 long mb = kb / 1024L;
329 kb = kb - (mb * 1024L);
330 nk = Math.round((kb / 1024d) * 100d);
331
332 return mb + "," + df.format(nk) + " MB";
333 }
334
335 protected void update() {
336 final int permilleFile;
337
338 if (sizeFile > 0) {
339 permilleFile = (int) (((double) bytesFile / (double) sizeFile) * 1000);
340 } else {
341 permilleFile = 0;
342 }
343
344 final int permilleTotal;
345
346 if (sizeTotal > 0) {
347 permilleTotal = (int) (((double) bytesTotal / (double) sizeTotal) * 1000);
348 } else {
349 permilleTotal = 0;
350 }
351
352 final String sFile = formatSize(bytesFile) + " von " + formatSize(sizeFile) + " übertragen";
353
354 lastUpdate = System.currentTimeMillis();
355 long now = (endTime > 0 ? endTime : lastUpdate);
356 final int sec = Math.max((int) (now - startTime) / 1000, 1);
357
358 long bytesPerMilli = 1;
359 if (now > startTime)
360 bytesPerMilli = Math.max(1, bytesTotal / (now - startTime));
361 int rest = (int) ((sizeTotal - bytesTotal) / bytesPerMilli / 1000);
362
363 int throughput = Math.round(bytesTotal / sec);
364
365 final String sTotal = formatSize(bytesTotal) + " von " + formatSize(sizeTotal) + " insgesamt übertragen";
366 final String sThrough = formatSize(throughput) + " pro Sekunde";
367 final String sName;
368
369 if (canceled) {
370 sName = "ABGEBROCHEN: Datei " + filename;
371 } else {
372 sName = "Datei " + filename;
373 }
374
375 final String sCounter;
376 final String sTime;
377
378 if (canceled) {
379 sCounter = "ABGEBROCHEN: " + fileCount + " von " + numFiles + " Dateien übertragen";
380 sTime = "Übertragung abgebrochen, Gesamtdauer " + formatTime(sec);
381 } else if (bytesTotal < sizeTotal) {
382 sCounter = "Übertrage Datei " + fileCount + " von " + numFiles;
383 sTime = "Dauer bisher " + formatTime(sec) + " / geschätzt noch " + formatTime(rest);
384 } else {
385 sCounter = "Alle " + numFiles + " Dateien übertragen";
386 sTime = "Übertragung beendet, Gesamtdauer " + formatTime(sec);
387 }
388
389 Runnable updater = new Runnable() {
390 public void run() {
391 lbFilename.setText(sName);
392 pbFile.setValue(permilleFile);
393 lbBytesFile.setText(sFile);
394 lbNumFiles.setText(sCounter);
395 lbBytesTotal.setText(sTotal);
396 pbTotal.setValue(permilleTotal);
397 lbThroughput.setText(sThrough);
398 lbTime.setText(sTime);
399
400 MCRUploadProgressMonitor.this.repaint();
401 }
402 };
403
404 SwingUtilities.invokeLater(updater);
405 }
406
407 /**
408 * Informs the monitor that the next file is being uploaded.
409 *
410 * @param name
411 * the name of the file without path
412 * @param size
413 * the byte size of the file
414 */
415 public void startFile(String name, long size) {
416 filename = name;
417 sizeFile = size;
418 bytesFile = 0;
419 fileCount++;
420 update();
421 }
422
423 /**
424 * Informs the monitor that a number of bytes was read for the current file.
425 * This is the single number of bytes read in this iteration, not the total
426 * number of files read from this file.
427 *
428 * @param bytesToAdd
429 * the single number of bytes transferred in this step
430 */
431 public void progressFile(long bytesToAdd) {
432 bytesFile += bytesToAdd;
433 bytesTotal += bytesToAdd;
434 update();
435 }
436
437 /**
438 * Informs the monitor that the upload of the current file is finished.
439 */
440 public void endFile() {
441 bytesFile = sizeFile;
442 update();
443 }
444
445 /**
446 * Informs the monitor that uploading of all files is finished.
447 */
448 public void finish() {
449 bytesFile = sizeFile;
450 bytesTotal = sizeTotal;
451 fileCount = numFiles;
452 finished = true;
453 endTime = System.currentTimeMillis();
454 end();
455 }
456
457 /**
458 * Informs the monitor that uploading is canceled because some error
459 * occured.
460 */
461 public void cancel(Exception ex) {
462 canceled = true;
463 finished = true;
464 endTime = System.currentTimeMillis();
465 MCRUploadProgressMonitor.reportException(ex);
466 end();
467 }
468
469 /**
470 * Shows a message dialog that displays an exception that occured.
471 *
472 * @param ex
473 * the Exception to be shown
474 */
475 public static void reportException(Exception ex) {
476 String title = "Fehler bei der Übertragung";
477 StringBuffer msg = new StringBuffer();
478 msg.append(title).append(":\n");
479 msg.append(ex.getClass().getName()).append("\n");
480 msg.append(ex.getLocalizedMessage()).append("\n");
481
482 if (ex instanceof MCRUploadException) {
483 MCRUploadException uex = (MCRUploadException) ex;
484 msg.append("Fehlermeldung des Servers:\n");
485 msg.append(uex.getServerSideClassName()).append("\n");
486 msg.append(uex.getMessage()).append("\n");
487 msg.append(uex.getServerSideStackTrace()).append("\n");
488 }
489
490 JOptionPane.showMessageDialog(null, msg, title, JOptionPane.ERROR_MESSAGE);
491 }
492
493 protected void end() {
494 finished = true;
495 button.setText("Schliessen");
496 button.setEnabled(true);
497
498 addWindowListener(new WindowAdapter() {
499 public void windowClosing(WindowEvent e) {
500 MCRUploadProgressMonitor.this.close();
501 }
502 });
503 update();
504 }
505
506 public boolean isCanceled() {
507 return canceled;
508 }
509
510 /**
511 * A small test that reads all files in a given directory.
512 *
513 * @param args
514 * first arg is the path of the directory to read from
515 * @throws Exception
516 * if anything goes wrong
517 */
518 public static void main(String[] args) throws Exception {
519 File dir = new File(args[0]);
520 File[] files = dir.listFiles();
521
522 int numFiles = files.length;
523 long sizeTotal = 0;
524
525 for (int i = 0; i < numFiles; i++)
526 sizeTotal += files[i].length();
527
528 MCRUploadProgressMonitor upm = new MCRUploadProgressMonitor(numFiles, sizeTotal, null);
529
530 for (int i = 0; i < numFiles; i++) {
531 if (upm.isCanceled())
532 break;
533 upm.startFile(files[i].getName(), files[i].length());
534
535 FileInputStream fin = new FileInputStream(files[i]);
536 byte[] buffer = new byte[65536];
537 long num = 0;
538
539 if (upm.isCanceled())
540 break;
541 while ((num = fin.read(buffer, 0, buffer.length)) != -1) {
542 // Simulate a read error and the following cancel() invocation
543 // if( i == 2 ) { upm.cancel( new java.io.IOException( "Simulierter Lesefehler" ) ); return; }
544
545 if (upm.isCanceled())
546 break;
547 upm.progressFile(num);
548 Thread.sleep(300); // Simulate network transfer time
549 if (upm.isCanceled())
550 break;
551 }
552
553 if (!upm.isCanceled())
554 upm.endFile();
555 }
556
557 if (!upm.isCanceled())
558 upm.finish();
559 }
560 }