English 中文(简体)
取消Java Webstart自定义下载
原标题:Cancel a Java Webstart custom download

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

由于没有可以调用的方法来取消下载,我试图了解在默认进度窗口中是如何完成的。由于使用ServiceManager实现,所以要找到实际的实现有点棘手。但我终于发现了这一点:[谷歌代码上的jdk源代码(DownloadServiceImpl)]

当你搜索“取消”或向下滚动到进度方法时,你会发现它应该像抛出RuntimeException一样简单。遗憾的是,这并没有真正奏效。它只是阻止调用progress方法。资源仍然在后台下载,loadPart方法永远不会返回。

如果你想自己尝试一下,我准备了一个小例子。不过,您需要某种网络服务器(当然,本地网络服务器就足够了)。我已经在带有Java 1.6.0_21(和apache tomcat 6)的Windows XP(32位)上尝试过这一点。

一个简单的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机器上,您会在“WindowsDriver Cachei386”下找到“driver.cab”。该文件必须添加到jar档案中(jar-cf largeResource.jar<;input-file>;)。

主程序如下所示(您需要将jnlp.jar作为lib包含在内,可以在<;jdk_home>;samplejnlpservlet中找到):

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服务器的公共文件夹中时,您应该能够通过web浏览器启动应用程序。然后单击下载按钮,在下载完成之前,单击下载窗口中的取消按钮。尝试自定义进度窗口后,您需要从Java缓存中删除应用程序(或仅删除资源)(因为无论单击取消按钮,资源都是在后台下载的)。

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

感谢任何帮助或提示。

Drax公司

问题回答

好的,看了一下你展示的谷歌样本,在课程的底部发现了这个

/* 
 * 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();
        }
    }
}

And what is interesting is that it looks like it sets the current thread s ThreadGroup as the application thread group. So this leads me to believe that by doing this the actual download is kept closer to the application (not sure what the correct terminology would be) such that the RuntimeException throw in the class in the cancel check really does affect it. Otherwise, my hunch is that in your application the download actually takes place in another thread and is "unaffected" by the Exception thrown by your application, hence, allowing it to complete.





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签