import java.util.jar.*;
import java.util.Enumeration;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.filechooser.*;
/** A general-purpose installer that detects the current architecture, and
* extracts appropriate architecture-specific files from its jar. This is
* useful for software packages that include system-specific native
* libraries. You should not need this installer if your application
* is architecture-independent. NOTE: processor architecture (i.e. x86 vs
* ARM) is not considered yet!
*
* The installer looks for particular directories from the jar it
* is packaged in, and extracts them. The directories are the following:
*
* generic/ - system-independent files
*
* Put any system-independent code and resources in this directory.
*
* win32/, win64/, linux32/, linux64/, mac32/, mac64/ - system-dependent files
*
* For each system for which you have system-specific resources and native
* libraries, define one of the directories above with the appropriate files
* in it. The intaller will detect the system and extract the appropriate
* directory. The absence of a directory signals that the respective system
* is not supported. So, if a system is supported but has no system-specific
* files, create an empty directory for it.
*
* By default, the installer produces a file requester letting the user
* specify the destination directory where the files should be installed.
*
* You must place JarInstaller.class in the root directory of the jar. It
* looks itself up in order to obtain a path to its jar.
*
*/
public class JarInstaller implements ActionListener {
static JarInstaller instance;
public JarInstaller() {}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand().toLowerCase();
if (cmd.startsWith("browse")) {
JFileChooser fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = fc.showOpenDialog(installdirtf);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
installdirtf.setText(""+file+ File.separator +
app_default_dir);
//This is where a real application would open the file.
//log.append("Opening: " + file.getName() + "." + newline);
} else {
//log.append("Open command cancelled by user." + newline);
}
} else if (cmd.startsWith("extract")) {
selected_dir = installdirtf.getText();
} else if (cmd.startsWith("cancel")) {
System.exit(0);
}
}
public static String getSystemArchitecture() {
String arch = System.getProperty("os.name").toLowerCase();
String bitness = System.getProperty("sun.arch.data.model");
if (arch.startsWith("linux")) {
return "linux"+bitness; // XXX assuming x86
}
if (arch.startsWith("windows")) {
return "win"+bitness;
}
if (arch.startsWith("mac")) {
return "mac"+bitness; // XXX assuming x86
}
return null;
}
/** Extracts all files from jar that are prefixed with dir. Destination
* is destdir. Returns the number of items (files+dirs) extracted.
*/
public static int extractJarDir(String jarfile,String dir,String destdir)
throws Exception {
int nr_extracted=0;
System.out.println("Extracting from: "+jarfile);
System.out.println("Subdirectory: "+dir);
JarInputStream jar = new JarInputStream(
new FileInputStream(jarfile) );
//JarFile jar = new JarFile(jarfile);
//Enumeration files = jar.entries();
//while (files.hasMoreElements()) {
//JarEntry jarentry = (JarEntry)files.nextElement();
while (true) {
JarEntry jarentry = (JarEntry)jar.getNextJarEntry();
if (jarentry==null) break;
String maindir = jarentry.getName();
// get rid of "./" and "/" prefix
if (maindir.startsWith("."))
maindir = maindir.substring(1);
if (maindir.startsWith(File.separator))
maindir = maindir.substring(1);
// skip META-INF
if (maindir.startsWith("META-INF")) continue;
// check if dir matches given dir pattern
if (maindir.startsWith(dir)) {
// chop dir pattern
maindir = maindir.substring(dir.length()+1);
// extract
File outfile = new File(destdir + File.separator + maindir);
System.out.println("Extracting: "+outfile);
// prepare for writing item, and write it
if (jarentry.isDirectory()) {
// replace file with directory?
if (outfile.exists() && outfile.isFile()) {
if (!confirmOverwrite()) {
throw new Exception("Extraction aborted.");
}
outfile.delete();
}
if (outfile.exists() && outfile.isDirectory()) {
// replace directory with directory? we need not do
// anything
} else if (!outfile.mkdir()) {
throw new Exception("Failed to create directory.");
}
nr_extracted++;
continue;
} else {
// replace directory with file -> not possible (yet)
if (outfile.isDirectory()) {
throw new Exception(
"A directory is in the way. "
+"Please remove files and directories and try again.");
}
// replace file with file -> ask first
if (outfile.exists()) {
if (!confirmOverwrite()) {
throw new Exception("Extraction aborted.");
}
outfile.delete();
}
// get the streams
//java.io.InputStream ins = jar.getInputStream(jarentry);
java.io.InputStream ins = jar;
java.io.FileOutputStream outs = new java.io.FileOutputStream(outfile);
// copy buffered (is a lot faster than one byte at a time)
byte[] buf = new byte[8192];
while (true) {
//outs.write(ins.read());
int len = ins.read(buf);
if (len < 0) break;
outs.write(buf,0,len);
}
nr_extracted++;
outs.close();
//ins.close();
}
} else {
//System.out.println("SKIPPED:"+maindir);
}
}
return nr_extracted;
}
// settings
static String app_name="MyApplication";
static String app_title=app_name + " Installer";
static String splashscreen = "install_splash.jpg";
static String app_default_dir = "MyApplication";
// gui elements
static JFrame frame;
static JTextField installdirtf;
static String selected_dir=null;
class InstallPanel extends JPanel {
Image bg;
InstallPanel() {
bg = new ImageIcon(getClass().getResource(splashscreen)).getImage();
//setSize(600,300);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bg,0,0,null);
g.setColor(new Color(255,255,255));
//g.drawString(install_text,100,100);
}
}
private static String getDestinationDirectory(String initialpath) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
frame = new JFrame(app_title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel0 = new JPanel();
panel0.setLayout(new BoxLayout(panel0,BoxLayout.Y_AXIS));
JPanel panel0b = new JPanel(new FlowLayout(FlowLayout.RIGHT));
JPanel panel1 = new JPanel();
panel1.setLayout(new BoxLayout(panel1,BoxLayout.X_AXIS));
JPanel panel2 = new JPanel();
panel2.setLayout(new BoxLayout(panel2,BoxLayout.Y_AXIS));
JPanel panel3a = new JPanel();
panel3a.setLayout(new BoxLayout(panel3a,BoxLayout.X_AXIS));
JPanel panel3b = new JPanel();
panel3b.setBackground(Color.white);
Border border = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
installdirtf = new JTextField(initialpath + File.separator
+ app_default_dir);
JButton browsebut = new JButton("Browse...");
browsebut.addActionListener(instance);
JButton extractbut = new JButton("Extract");
extractbut.addActionListener(instance);
JButton cancelbut = new JButton("Cancel");
cancelbut.addActionListener(instance);
java.net.URL imageURL = instance.getClass().getResource(splashscreen);
JLabel msglab = new JLabel(
""
+"Extract Program
"
+"This will extract "+app_name+" to a directory"
+" on your system."
+"
"
+"Select the installation directory below."
+"");
//msglab.setSize(300,270);
msglab.setPreferredSize(new Dimension(400,270));
msglab.setVerticalAlignment(SwingConstants.TOP);
JLabel splashlab = new JLabel(new ImageIcon(imageURL));
splashlab.setBorder(border);
panel1.add(splashlab);
panel1.add(panel2);
//JScrollPane msglabsc = new JScrollPane(msglab);
//msglabsc.setPreferredSize(new Dimension(300,270));
panel2.setBorder(border);
panel3a.add(installdirtf);
panel3a.add(browsebut);
panel3b.add(msglab);
panel2.add(panel3b);
panel2.add(panel3a);
//lab.setIconTextGap(0);
//panel2.add(new JarInstaller().new InstallPanel());
//panel2.setPreferredSize(new Dimension(600,300));
panel0.add(panel1);
panel0.add(panel0b);
panel0b.add(extractbut);
panel0b.add(cancelbut);
panel0b.setBorder(border);
frame.add(panel0);
frame.pack();
frame.setVisible(true);
selected_dir = null;
try {
while (selected_dir==null) {
Thread.sleep(500);
}
} catch (InterruptedException e) {}
//System.out.println(selected_dir);
//System.exit(0);
return selected_dir;
}
private static void showError(String error) {
JOptionPane.showMessageDialog(frame,
error, "Error", JOptionPane.ERROR_MESSAGE);
}
private static void showMessage(String msg) {
JOptionPane.showMessageDialog(frame, msg);
}
private static boolean overwriteConfirmed=false;
private static boolean confirmOverwrite() {
if (overwriteConfirmed) return true;
if (!requestYesNo("Files will be overwritten! Continue?",
"Continue","Cancel")) return false;
overwriteConfirmed = true;
return true;
}
private static boolean requestYesNo(String msg,String yesMsg,String noMsg) {
Object [] options = new String[] {yesMsg,noMsg};
int n = JOptionPane.showOptionDialog(frame,
msg, msg,
JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
null, options, options[0]);
return n == 0;
}
/** this should pop up a requester for system architecture */
private static String requestSystemArch() {
return "win32";
}
// http://stackoverflow.com/questions/1529611/how-to-write-a-java-program-which-can-extract-a-jar-file-and-store-its-data-in-s
public static void main(String [] args) {
instance = new JarInstaller();
// to get the URL of the jar, get the URL of a known resource inside
// the jar. XXX I did not find a method for doing this without such a
// known resource.
String myclass = instance.getClass().getName();
String mypath = ClassLoader.getSystemClassLoader()
.getResource(myclass+".class").getPath();
// the path is anything after a protocol specification and
// before the last 'jar!'
int startjar = mypath.indexOf(":"); // -1 = start of string
int endjar = mypath.lastIndexOf("jar!");
if (endjar >= 0) {
String myjar = mypath.substring(startjar+1, endjar+3);
String destination = getDestinationDirectory(
new File(myjar).getParent());
String arch = getSystemArchitecture();
if (arch==null) arch = requestSystemArch();
try {
// extract architecture-specific files
int extracted = extractJarDir(myjar,arch,destination);
if (extracted == 0) {
showError("System architecture not supported");
}
// extract generic files (may be 0)
extracted = extractJarDir(myjar,"generic",destination);
showMessage("Finished!");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
showError("I/O error extracting files.");
} catch (Exception e) {
showError(e.getMessage());
}
} else {
showError("JarInstaller cannot find its Jar!");
}
System.exit(0);
}
}