Some polish for self extracting jar

- show progress bar while unpacking.
- show error popup on any problem.
This commit is contained in:
Kris De Volder
2019-10-17 14:45:44 -07:00
parent 9ebac23419
commit 44db867715
5 changed files with 134 additions and 22 deletions

View File

@@ -18,12 +18,6 @@
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>

View File

@@ -9,6 +9,12 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import ui.ProgressApi;
import ui.ProgressBar;
/**
* SelfExtractor
@@ -21,7 +27,19 @@ public class SelfExtractor {
byte[] buffer = new byte[BUF_SIZE];
public static void main(String[] args) throws Exception {
new SelfExtractor().run(args);
try {
new SelfExtractor().run(args);
System.exit(0);
} catch (Throwable e) {
e.printStackTrace();
String message = e.getMessage();
if (message==null) {
message = e.getClass().getName();
}
JOptionPane.showMessageDialog(new JFrame(), message, "Error",
JOptionPane.ERROR_MESSAGE);
System.exit(-1);
}
}
private void run(String[] args) throws Exception {
@@ -46,7 +64,7 @@ public class SelfExtractor {
}
repackagedPath = repackagedPath + ".self-extracting.jar";
System.out.println(" as "+repackagedPath);
File wrapperJar = getMyJar();
System.out.println("wrapperJar = "+wrapperJar);
try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(wrapperJar))) {
@@ -85,20 +103,20 @@ public class SelfExtractor {
private File getMyJar() {
Class<?> myklass = this.getClass();
String resourceName = this.getClass().getName() + ".class";
URL resource = myklass.getResource(resourceName);
String myResourceUrl = resource.toString();
//Example: jar:file:/home/.../self-extracing-jar-creator-0.0.1-SNAPSHOT.jar!/SelfExtractor.class
if (!myResourceUrl.startsWith("jar:file:")) {
//For convenience in 'development mode' look for a local jar built by maven.
File devJar = new File("target/self-extracting-jar-creator-0.0.1-SNAPSHOT.jar");
if (devJar.isFile()) {
return devJar;
}
throw new IllegalStateException("To create self extracting jar, you must run this code from a jar");
}
return new File(myResourceUrl.substring("jar:file:".length(), myResourceUrl.indexOf('!')));
}
URL resource = myklass.getResource(resourceName);
String myResourceUrl = resource.toString();
//Example: jar:file:/home/.../self-extracing-jar-creator-0.0.1-SNAPSHOT.jar!/SelfExtractor.class
if (!myResourceUrl.startsWith("jar:file:")) {
//For convenience in 'development mode' look for a local jar built by maven.
File devJar = new File("target/self-extracting-jar-creator-0.0.1-SNAPSHOT.jar");
if (devJar.isFile()) {
return devJar;
}
throw new IllegalStateException("To create self extracting jar, you must run this code from a jar");
}
return new File(myResourceUrl.substring("jar:file:".length(), myResourceUrl.indexOf('!')));
}
private ZipInputStream openContentsZip() {
InputStream zip = this.getClass().getResourceAsStream(CONTENTS_ZIP_RSRC);
if (zip==null) {
@@ -109,6 +127,9 @@ public class SelfExtractor {
public void unpack() throws Exception {
File destDir = new File(System.getProperty("user.dir"));
int numEntries = getNumEntries();
int processed = 0;
ProgressApi progress = ProgressBar.create(numEntries);
try (ZipInputStream zip = openContentsZip()) {
ZipEntry entry;
while ((entry = zip.getNextEntry())!=null) {
@@ -127,7 +148,22 @@ public class SelfExtractor {
copy(zip, out);
}
}
processed++;
System.out.println("Progress = "+processed +"/"+numEntries);
progress.worked(1);
}
} finally {
progress.done();
}
}
private int getNumEntries() throws IOException {
int count = 0;
try (ZipInputStream zip = openContentsZip()) {
while (zip.getNextEntry()!=null) {
count++;
}
}
return count;
}
}

View File

@@ -0,0 +1,8 @@
package ui;
public interface ProgressApi {
void worked(int amount);
void done();
}

View File

@@ -0,0 +1,74 @@
package ui;
import java.awt.BorderLayout;
import java.util.concurrent.CompletableFuture;
import javax.swing.BorderFactory;
import javax.swing.BoundedRangeModel;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class ProgressBar extends JPanel {
private JProgressBar progressBar;
public ProgressBar(BoundedRangeModel model) {
super(new BorderLayout());
progressBar = new JProgressBar(model);
progressBar.setValue(0);
progressBar.setStringPainted(true);
JPanel panel = new JPanel();
panel.add(progressBar);
add(panel, BorderLayout.CENTER);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
}
public static ProgressApi create(int totalWork) {
BoundedRangeModel model = new DefaultBoundedRangeModel(0, 0, 0, totalWork);
CompletableFuture<JFrame> window = new CompletableFuture<>();
final ProgressApi api = new ProgressApi() {
@Override
public void worked(int amount) {
model.setValue(model.getValue()+amount);
}
@Override
public void done() {
window.thenAccept(w -> SwingUtilities.invokeLater(() -> {
w.setVisible(false);
w.dispose();
}));
}
};
SwingUtilities.invokeLater(() -> {
try {
//Create and set up the window.
JFrame frame = new JFrame("Unpacking...");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new ProgressBar(model);
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
window.complete(frame);
} catch (Throwable e) {
window.completeExceptionally(e);
}
});
return api;
}
}