Отмена пользовательской загрузки Java Webstart - PullRequest
1 голос
/ 14 октября 2010

при загрузке ресурса в приложении Java Webstart обычно отображается окно прогресса загрузки, в котором отображается ход загрузки.Если это окно является окном хода выполнения по умолчанию, оно имеет кнопку отмены.Я в основном пытаюсь реализовать эту кнопку отмены в пользовательском окне прогресса загрузки.

Поскольку нет способа, который вы могли бы вызвать, чтобы отменить загрузку, я попытался выяснить, как это было сделано в ходе выполнения по умолчанию.окно.Из-за реализации с ServiceManager немного сложно найти реальную реализацию.Но я наконец нашел это: [jdk-source на googlecode (DownloadServiceImpl)] .

Когда вы будете искать «отмена» или просто прокрутите вниз до метода выполнения, вы увидите, что это должно быть так же просто, как и генерирование RuntimeException.К сожалению, это на самом деле не работает.Это просто останавливает метод прогресса от вызова.Ресурс по-прежнему загружается в фоновом режиме, и метод loadPart никогда не возвращается.

Если вы хотите попробовать это сами, я подготовил небольшой пример.Вам понадобится какой-то веб-сервер (хотя локального веб-сервера вполне достаточно).Я пробовал это на Windows XP (32-разрядной) с Java 1.6.0_21 (и Apache Tomcat 6).

Простой файл jnlp будет выглядеть так (вы, вероятно, захотите изменить порт):

<?xml version="1.0" encoding="utf-8"?>
<jnlp 
  spec="1.0+"
  codebase="http://127.0.0.1:8080/DownloadTest" 
  href="DownloadTest.jnlp" 
  version="1.0">

  <information>
    <title>DownloadTest</title>
    <vendor>Download Tester</vendor>
  </information>

  <resources os="Windows">
    <java version="1.6.0_18+" href="http://java.sun.com/products/autodl/j2se" />
    <jar href="DownloadTest.jar" main="true"/>
    <jar href="largeResource.jar" download="lazy" part="One"/>
  </resources>

  <application-desc main-class="downloadtest.Main">
  </application-desc>
</jnlp>

Далее вам потребуется большой файл в качестве ресурса (содержимое не имеет значения ввсе).Например, на многих компьютерах с Windows вы найдете «driver.cab» в «Windows \ Driver Cache \ i386».Файл должен быть добавлен в архив jar (jar -cf largeResource.jar <input file>).

Основная программа выглядит следующим образом (вам нужно включить jnlp.jar в качестве lib, который вы можете найти по адресу <jdk_home>\sample\jnlp\servlet):

package downloadtest;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.jnlp.DownloadService;
import javax.jnlp.DownloadServiceListener;
import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingWorker;

public class Main {
    private static DownloadService downloadService;
    private static DownloadServiceListener customDownloadWindow;

    static {
        try {
            downloadService = (DownloadService) ServiceManager.lookup("javax.jnlp.DownloadService");
        } catch (UnavailableServiceException ex) {
            System.err.println("DownloadService not available.");
        }
        customDownloadWindow = new CustomProgress();
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("DownloadTest");
        frame.setBounds(0, 0, 200, 100);
        frame.setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
        frame.setLayout(null);
        JButton startDownload = new JButton("download");
        startDownload.setBounds(20, 20, 150, 40);
        startDownload.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() {
                        try {
                            downloadService.loadPart("One", customDownloadWindow);
                            //downloadService.loadPart("One", downloadService.getDefaultProgressWindow());
                        } catch (IOException ex) {
                            ex.printStackTrace();
                            System.err.println("IOException loadPart.");
                        }
                        return null;
                    }
                }.execute();
            }
        });
        frame.add(startDownload);
        frame.setVisible(true);
    }
}

Вы можете попробовать каждое окно прогресса загрузкираскомментировав одну строку «downloadService.loadPart ...» и закомментировав другую.

И, наконец, собственно собственное окно прогресса:

package downloadtest;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.jnlp.DownloadServiceListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

public class CustomProgress implements DownloadServiceListener {
    JFrame frame = null;
    JProgressBar progressBar = null;
    boolean uiCreated = false;
    boolean canceled = false;

    public CustomProgress() {
    }

    private void create() {
        JPanel top = createComponents();
        frame = new JFrame(); // top level custom progress indicator UI
        frame.getContentPane().add(top, BorderLayout.CENTER);
        frame.setBounds(300,300,400,300);
        frame.pack();
        updateProgressUI(0);
    }

    private JPanel createComponents() {
        JPanel top = new JPanel();
        top.setBackground(Color.WHITE);
        top.setLayout(new BorderLayout(20, 20));

        String lblText = "<html><font color=green size=+2>JDK Documentation</font>" +
                   "<br/> The one-stop shop for Java enlightenment! <br/></html>";
        JLabel lbl = new JLabel(lblText);
        top.add(lbl, BorderLayout.NORTH);

        progressBar = new JProgressBar(0, 100);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        top.add(progressBar, BorderLayout.CENTER);

        JButton cancelButton = new JButton("Cancel");
        cancelButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                CustomProgress.this.canceled = true;
            }
        });
        top.add(cancelButton, BorderLayout.SOUTH);

        return top;
    }

    public void progress(URL url, String version, long readSoFar,
                         long total, int overallPercent) {
        updateProgressUI(overallPercent);

    }

    public void upgradingArchive(java.net.URL url,
                      java.lang.String version,
                      int patchPercent,
                      int overallPercent) {
        updateProgressUI(overallPercent);
    }

    public void validating(java.net.URL url,
                java.lang.String version,
                long entry,
                long total,
                int overallPercent) {
        updateProgressUI(overallPercent);
    }


    public void downloadFailed(URL url, String string) {
        System.err.println("Download failed");
    }

    private void updateProgressUI(int overallPercent) {
        if (overallPercent > 0 && overallPercent < 99) {
            if (!uiCreated) {
                uiCreated = true;
                // create custom progress indicator's UI only if
                // there is more work to do, meaning overallPercent > 0 and < 100
                // this prevents flashing when RIA is loaded from cache
                create();
            }
            progressBar.setValue(overallPercent);
            if (canceled) {
                throw new RuntimeException("canceled by user");
            }
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    frame.setVisible(true);
                }
            });
        } else {
            // hide frame when overallPercent is above 99
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    if (frame != null) {
                        frame.setVisible(false);
                        frame.dispose();
                    }
                }
            });
        }
    }
}

Это в основном взято из руководства Oracle(http://download.oracle.com/javase/tutorial/deployment/webstart/customProgressIndicatorForAppln.html). Я только что добавил кнопку отмены.

Когда вы создадите этот файл в виде jar-файла и поместите его вместе с largeResource.jar и DownloadTest.jnlp в общую папку вашего веб-сервера, вы сможетечтобы запустить приложение через веб-браузер. Затем нажмите кнопку загрузки и, прежде чем она будет завершена, нажмите кнопку отмены в окне загрузки. После того, как вы попробуете пользовательское окно прогресса, вам нужно будет удалить приложение (или только ресурс) из вашей Javaкеш (поскольку ресурс загружается в фоновом режиме независимо от нажатия кнопки отмены).

Итак, почему яэто работает с окном прогресса по умолчанию, но не с пользовательским окном прогресса?Есть ли простая возможность отменить загрузку с помощью специального окна загрузки?

Любая помощь или полезные советы приветствуются.

Drax

1 Ответ

0 голосов
/ 14 октября 2010

Хорошо, взглянули на пример Google, который вы показали, и нашли его внизу класса

/* 
 * Progress Helper class
 *
 * The DownloadServiceListerner interface defined in the JNLP API is 
 * a subset of the DownloadProgressWindow interface used by elsewhere.
 *
 * this class is used to create a Helper object that implements both.
 */
private class ProgressHelper extends CustomProgress {


    private DownloadServiceListener _dsp = null;

    public ProgressHelper() {
        _dsp = null;
    }

    public ProgressHelper(DownloadServiceListener dsp) {
        setAppThreadGroup(Thread.currentThread().getThreadGroup());
        setListener(dsp);
        _dsp = dsp;
        if (_dsp instanceof DefaultProgressHelper) {
            ((DefaultProgressHelper) _dsp).initialize();
        }
        // for bug #4432604:
        _dsp.progress(null, null, 0, 0, -1);
    }

    public void done() {
        if (_dsp instanceof DefaultProgressHelper) {
            ((DefaultProgressHelper) _dsp).done();
        } else {
            // make sure callbacks to DownloadServiceListener have
            // been called before returning (for TCK test)
            flush();
        }
    }
}

И что интересно, похоже, что он устанавливает текущий поток ThreadGroup в качестве группы потоков приложения. Таким образом, это заставляет меня поверить, что при этом фактическая загрузка поддерживается ближе к приложению (не уверен, какой будет правильная терминология), так что бросок RuntimeException в классе в проверке отмены действительно влияет на него. В противном случае моя догадка заключается в том, что в вашем приложении загрузка фактически происходит в другом потоке и "не зависит" от Exception, выданного вашим приложением, следовательно, позволяя завершить его.

...