java - 取消 Java Webstart 自定义下载

标签 java jnlp java-web-start

当您在 Java Webstart 应用程序中下载资源时,通常会显示一个下载进度窗口,其中显示下载的进度。如果此窗口是默认进度窗口,则它有一个取消按钮。我基本上是在尝试在自定义下载进度窗口中实现此取消按钮。

由于没有可以调用的方法来取消下载,因此我尝试找出如何在默认进度窗口中完成此操作。由于使用 ServiceManager 实现,因此找到实际的实现有点棘手。但我终于找到了这个:[jdk-source on 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 计算机上,您会在“Windows\Driver Cache\i386”下找到“driver.cab”。该文件必须添加到 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 放在 Web 服务器的公共(public)文件夹中时,您应该能够通过 Web 浏览器启动该应用程序。然后单击下载按钮,并在完成之前单击下载窗口中的取消按钮。尝试自定义进度窗口后,您需要从 Java 缓存中删除应用程序(或只是资源)(因为无论单击取消按钮,资源都会在后台下载)。

那么,为什么这适用于默认进度窗口而不适用于自定义进度窗口?是否可以通过自定义下载窗口轻松取消下载?

任何帮助或提示表示赞赏。

德拉克斯

最佳答案

好的,查看了您展示的 Google 示例,并在类(class)底部找到了该示例

/* 
 * 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 的“影响”,因此允许它完成。

关于java - 取消 Java Webstart 自定义下载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3933108/

相关文章:

java - 在 Intellij 中打开 Nullable 警告

java - 保留不是 NodeEntity 属性的 RelationshipEntity

java - OSX Mavericks 打开 .jnlp 1.7 U 45

Java - 如何在 JNLP 文件中自动获取 IP 地址?

java - 重写 URLConnection 的 getInputStream 以通过自定义协议(protocol)接收数据

java - 我的 Jlabel 中的文本和图片之间的正方形 [_]

java - 从java对象实例生成XSD的框架

java - "Unable to create shortcut for"在 OSX 上启动 Java7

java - 启动 jnlp 文件时出错 : Could not create the Java Virtual Machine and A fatal exception has occurred. 程序将退出

java - 如何在 java web 客户端中实现基本操作系统风格的控制台?