2010-10-14 3 views
1

Lorsque vous téléchargez une ressource dans une application Java Webstart, une fenêtre de progression du téléchargement s'affiche généralement, indiquant la progression du téléchargement. Si cette fenêtre est la fenêtre de progression par défaut, elle possède un bouton d'annulation. J'essaye fondamentalement d'implémenter ce bouton d'annulation dans une fenêtre de progression de téléchargement faite sur commande.Annuler un téléchargement personnalisé Java Webstart

Comme il n'y a aucune méthode que vous pourriez appeler pour annuler le téléchargement, j'ai essayé de trouver comment cela a été fait dans la fenêtre de progression par défaut. En raison de la mise en œuvre avec un ServiceManager, il est un peu difficile de trouver l'implémentation réelle. Mais j'ai finalement trouvé ceci: [jdk-source on googlecode (DownloadServiceImpl)].

Lorsque vous recherchez "annuler" ou que vous descendez simplement à la méthode de progression, vous verrez que cela devrait être aussi facile que de lancer une RuntimeException. Malheureusement, cela ne fonctionne pas vraiment. Cela arrête simplement la méthode de progression d'être appelée. La ressource est toujours téléchargée en arrière-plan et la méthode loadPart ne retourne jamais.

Si vous voulez essayer cela par vous-même, j'ai préparé un petit exemple. Vous aurez besoin d'une sorte de serveur web (un serveur web local est suffisant bien sûr). J'ai essayé ceci sur Windows XP (32 bits) avec Java 1.6.0_21 (et apache tomcat 6).

Un fichier jnlp simple, ressemblerait à ceci (vous voulez probablement changer le port):

<?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> 

Ensuite, vous aurez besoin d'un grand fichier en tant que ressource (le contenu n'a pas d'importance du tout). Par exemple sur de nombreuses machines Windows, vous trouverez "driver.cab" sous "Windows \ Driver Cache \ i386". Le fichier doit être ajouté à une archive jar (jar -cf largeResource.jar <input file>).

Le programme principal ressemble à ceci (vous devrez inclure jnlp.jar comme lib, que vous pouvez trouver à <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); 
    } 
} 

Vous pouvez essayer chaque fenêtre de progression de téléchargement en décommentant un « DownloadService. loadPart ... "ligne et commenter l'autre.

Et enfin la fenêtre de progression personnalisée lui-même:

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

Ceci est essentiellement tiré d'un tutoriel Oracle (http://download.oracle.com/javase/tutorial/deployment/webstart/customProgressIndicatorForAppln.html) . Je viens d'ajouter un bouton d'annulation. Lorsque vous générez ce fichier jar et que vous le placez avec les fichiers largeResource.jar et DownloadTest.jnlp dans un dossier public de votre serveur Web, vous devez pouvoir démarrer l'application via votre navigateur Web. Ensuite, cliquez sur le bouton de téléchargement et avant qu'il ne soit terminé, cliquez sur le bouton Annuler dans la fenêtre de téléchargement. Après avoir essayé la fenêtre de progression personnalisée, vous devez supprimer l'application (ou simplement la ressource) de votre cache Java (car la ressource est téléchargée en arrière-plan, même si vous cliquez sur le bouton Annuler). Alors, pourquoi cela fonctionne-t-il avec la fenêtre de progression par défaut, mais pas avec la fenêtre de progression personnalisée? Y a-t-il une possibilité facile d'annuler un téléchargement avec une fenêtre de téléchargement personnalisée?

Toute aide ou conseil apprécié.

Drax

Répondre

0

Ok, a pris un coup d'œil à l'échantillon Google que vous avez montré et trouvé cela au fond de la classe

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

Et ce qui est intéressant est qu'il ressemble à elle fixe de thread en cours ThreadGroup en tant que groupe de threads d'application. Cela m'amène à croire que, ce faisant, le téléchargement réel est plus proche de l'application (je ne sais pas quelle serait la bonne terminologie), de sorte que le lancer RuntimeException dans la classe dans le contrôle d'annulation l'affecte vraiment. Sinon, mon intuition est que dans votre application, le téléchargement se déroule réellement dans un autre thread et est "non affecté" par le Exception lancé par votre application, ce qui lui permet de se terminer.

+0

Eh bien je pourrais définir un DefaultUncaughtExceptionHandler pour tous les threads ('Thread.setDefaultUncaughtExceptionHandler (new DownloadUncaughtExceptionHandler())' 'qui est probablement similaire à votre solution (sinon la même chose) et mettre la méthode loadPart dans un thread au lieu d'un SwingWorker. De cette façon, le gestionnaire d'exceptions serait appelé, mais le problème demeure que le téléchargement de la ressource continue en arrière-plan. – Drax

+0

Le téléchargement a-t-il lieu dans un autre thread? Pouvez-vous obtenir une poignée sur le fil? –

Questions connexes