/*
* Created on 7 mai 2004
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
package jsynoptic.installer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import jsynoptic.installer.resources.InstallerResources;
import simtools.ui.CustomizedLocale;
/**
* @author nicolas
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
class PackagesPanel extends InstallerPanel {
/**
* A looger to dump error or warning messages in a soket, an output stream, a file...
*/
static Logger _logger = simtools.util.LogConfigurator.getLogger(PackagesPanel.class.getName());
protected ArrayList boxes;
protected int numPackages;
protected File location;
protected JTextField tfLoc;
protected JButton btnLoc;
protected JEditorPane tipPane;
protected JScrollPane tipScroll;
protected JProgressBar progress;
// fields protected by mutex => why can't we synchronize a field on the = current Object?
protected boolean installFinished;
protected Thread installThread;
protected String os;
public PackagesPanel(Installer installer) {
super(installer);
setLayout(new BorderLayout());
boxes = new ArrayList();
numPackages = Long.decode(Installer.resources.getString("numPackages")).intValue();
// Simple OS detection => enough for our purpose to get default location
if (File.separatorChar == '/') os = "Unix";
else if (File.separatorChar == '\\') os = "Windows";
else os = "Other";
JPanel packSection = new JPanel(new BorderLayout());
JPanel boxPanel = new JPanel(new GridLayout(numPackages, 1));
JPanel scrollableBoxPanel = new JPanel(new BorderLayout());
// Add all packages
for (int i=1; i<=numPackages; ++i) {
// 0 => unckecked, 1 => checked, 2 => mandatory (and thus checked)
int defaultState = Long.decode(Installer.resources.getString("packageDefaultState"+i)).intValue();
String name;
try {
name = Installer.resources.getString("packageName"+i+os);
} catch (MissingResourceException e1) {
name = Installer.resources.getString("packageName"+i);
}
JCheckBox cb = new JCheckBox(name, defaultState>=1);
cb.addMouseListener(new TipListener("packageTip"+i));
if (defaultState==2) cb.setEnabled(false);
boxes.add(cb);
cb.setAlignmentX(Component.LEFT_ALIGNMENT);
boxPanel.add(cb);
}
scrollableBoxPanel.add(boxPanel, BorderLayout.NORTH);
packSection.add(new JScrollPane(scrollableBoxPanel),BorderLayout.WEST);
tipPane = new JEditorPane();
tipPane.setEditable(false);
tipPane.setAutoscrolls(false);
tipPane.setContentType("text/html");
tipPane.setText(Installer.resources.getString("pointPackageForDescription"));
packSection.add(tipScroll = new JScrollPane(tipPane),BorderLayout.CENTER);
packSection.add(progress = new JProgressBar(), BorderLayout.SOUTH);
add(packSection,BorderLayout.CENTER);
Box hbox = Box.createHorizontalBox();
hbox.add(new JLabel(Installer.resources.getString("location")));
location = new File(Installer.resources.getString("defaultLocation"+os));
hbox.add(tfLoc = new JTextField());
updateLoc();
Dimension d = tfLoc.getPreferredSize();
d.width = getWidth();
tfLoc.setMaximumSize(d);
hbox.add(btnLoc = new JButton(Installer.resources.getString("chooseLocation")));
add(hbox,BorderLayout.SOUTH);
btnLoc.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser(location);
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int res = fc.showOpenDialog(PackagesPanel.this.installer);
if (res==JFileChooser.APPROVE_OPTION) {
// first choose selection
location = fc.getSelectedFile();
// if user entered directory but no selection, choose it instead
if (location==null) location = fc.getCurrentDirectory();
updateLoc();
}
}
});
installFinished = false;
installThread = null;
}
/* (non-Javadoc)
* @see jsynoptic.installer.InstallerPanel#process()
*/
public boolean process() {
boolean run = false;
synchronized(this) {
if (installFinished) run = true;
else {
if (installThread!=null) return false;
installThread = new Thread() {
public void run() {
install();
}
};
}
}
if (run) {
runJSynoptic();
return true;
}
installThread.start();
return false;
}
protected void runJSynoptic() {
new Thread() {
public void run() {
installer.writeProperties();
location.getAbsolutePath();
File jsynoptic;
if (os.equals("Unix")) jsynoptic = new File(location,"jsynoptic.sh");
else if (os.equals("Windows")) jsynoptic = new File(location,"jsynoptic.exe");
else jsynoptic = null;
if ((jsynoptic!=null) && jsynoptic.exists()) {
try {
Runtime.getRuntime().exec(jsynoptic.getAbsolutePath());
// terminate this JVM, the other process should run in parallel
System.exit(0);
} catch (IOException e1) {
// see below
}
}
ResourceBundle runRes = ResourceBundle.getBundle("jsynoptic.ui.resources.RunResources",CustomizedLocale.get());
// try manually run the jar file from this JVM
// of course, this supposes the jar file is present
File jsynJar = new File(location,"jsynoptic-" + runRes.getString("productVersion") + ".jar");
if (!jsynJar.exists()) {
JOptionPane.showMessageDialog(
installer,
Installer.resources.getString("cantFindJSynoptic"),
Installer.resources.getString("errorInInstall"),
JOptionPane.ERROR_MESSAGE
);
return;
}
// remove this installer
installer.dispose();
// Now the fun part => run jsynoptic
// first add the jsynoptic jar file to the classpath
URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method method;
try {
method = URLClassLoader.class.getDeclaredMethod("addURL",new Class[]{URL.class});
method.setAccessible(true);
method.invoke(sysloader,new Object[]{ jsynJar.toURL() });
// So, jsynoptic jar should be added in the class path now
method = Class.forName("jsynoptic.ui.Run").getDeclaredMethod("main",new Class[]{String[].class});
// invoke the main function
method.invoke(null,new Object[]{new String[]{}});
// Main exited but JSynoptic main window sould be present
// since the installer window is out, JSynoptic should be in charge now
} catch (Exception e) {
// window already destroyed => use err channel
System.err.println(Installer.resources.getString("installErrorIs")+e.getLocalizedMessage());
System.err.println(Installer.resources.getString("errorInInstall"));
System.exit(1); // bad luck :-(
}
}
}.start();
}
public void install() {
location = new File(tfLoc.getText());
if (!location.exists()) {
location.mkdirs();
}
if (!location.isDirectory())
location = new File(location.getParentFile(), "jsynoptic");
tfLoc.setText(location.getAbsolutePath());
boolean overwriteAll = false;
byte[] data = new byte[4096]; // small array allows progress bar to move
installer.previous.setEnabled(false);
installer.next.setEnabled(false);
tfLoc.setEnabled(false);
btnLoc.setEnabled(false);
try {
// Install all checked packages
for (int i=1; i<=numPackages; ++i) {
JCheckBox cb = (JCheckBox)boxes.get(i-1);
if (!cb.isSelected()) continue;
cb.setBackground(Color.red);
String zipName;
try {
if (installer.jre==null) {
try {
zipName = Installer.resources.getString("packageZip"+i+os+"NoJRE");
} catch (MissingResourceException e) {
zipName = Installer.resources.getString("packageZip"+i+os);
}
} else {
zipName = Installer.resources.getString("packageZip"+i+os);
}
} catch (MissingResourceException e1) {
zipName = Installer.resources.getString("packageZip"+i);
}
InputStream is = InstallerResources.class.getResourceAsStream(zipName);
if (is==null) throw new IOException();
ZipInputStream zip = new ZipInputStream(is);
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
File f = new File(location, entry.getName());
if (entry.isDirectory()) {
f.mkdirs(); // TODO: handle installation failed
continue;
}
if (f.exists() && (!overwriteAll)) {
Object[] options = new Object[] {
Installer.resources.getString("yes"),
Installer.resources.getString("yesAll"),
Installer.resources.getString("no"),
Installer.resources.getString("cancel"),
};
int res = JOptionPane.showOptionDialog(
installer,
Installer.resources.getString("overwriteFile")+f.getAbsolutePath(),
Installer.resources.getString("overwriteTitle"),
JOptionPane.DEFAULT_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (res==2) continue;
if ((res==JOptionPane.CLOSED_OPTION) || (res == 3)) break; // TODO: handle installation failed
if (res==1) overwriteAll = true;
}
long size = entry.getSize();
if (size!=-1) {
progress.setIndeterminate(false);
progress.setMinimum(0);
progress.setMaximum((int)size);
progress.setValue(0);
}
else progress.setIndeterminate(true);
progress.setString(f.getAbsolutePath());
progress.setStringPainted(true);
FileOutputStream fos = new FileOutputStream(f);
int nread;
while ((nread = zip.read(data)) !=-1) {
fos.write(data,0,nread);
try {
SwingUtilities.invokeAndWait(new BarUpdater(nread));
} catch (InterruptedException e1) {
} catch (InvocationTargetException e1) {
}
}
fos.flush();
fos.close();
}
cb.setBackground(Color.green);
}
// Extract JRE from installer's ZIP if present => gives feedback
if (installer.zip!=null) {
ZipFile zip = new ZipFile(installer.zip);
ZipEntry entry = zip.getEntry("jre");
// Overkill: entry exists and was checked by the installer main function
if (entry==null) throw new IOException("Internal Error!");
Enumeration enumEntries = zip.entries();
while (enumEntries.hasMoreElements()) {
entry = (ZipEntry)enumEntries.nextElement();
if (!entry.getName().startsWith("jre")) continue;
File f = new File(location, entry.getName());
if (entry.isDirectory()) {
f.mkdirs(); // TODO: handle installation failed
continue;
}
if (f.exists() && (!overwriteAll)) {
Object[] options = new Object[] {
Installer.resources.getString("yes"),
Installer.resources.getString("yesAll"),
Installer.resources.getString("no"),
Installer.resources.getString("cancel"),
};
int res = JOptionPane.showOptionDialog(
installer,
Installer.resources.getString("overwriteFile")+f.getAbsolutePath(),
Installer.resources.getString("overwriteTitle"),
JOptionPane.DEFAULT_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (res==2) continue;
if ((res==JOptionPane.CLOSED_OPTION) || (res == 3)) break; // TODO: handle installation failed
if (res==1) overwriteAll = true;
}
long size = entry.getSize();
if (size!=-1) {
progress.setIndeterminate(false);
progress.setMinimum(0);
progress.setMaximum((int)size);
progress.setValue(0);
}
else progress.setIndeterminate(true);
progress.setString(Installer.resources.getString("installingJRE")+" : "+f.getAbsolutePath());
progress.setStringPainted(true);
FileOutputStream fos = new FileOutputStream(f);
InputStream zipStream = zip.getInputStream(entry);
int nread;
while ((nread = zipStream.read(data)) !=-1) {
fos.write(data,0,nread);
try {
SwingUtilities.invokeAndWait(new BarUpdater(nread));
} catch (InterruptedException e1) {
} catch (InvocationTargetException e1) {
}
}
fos.flush();
fos.close();
}
zip.close();
}
// Else copy file by file the JRE directory
else if (installer.jre!=null) {
progress.setMinimum(0);
progress.setMaximum(1);
progress.setValue(0);
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
progress.setIndeterminate(true);
progress.setString(Installer.resources.getString("installingJRE"));
}
});
} catch (InterruptedException e1) {
} catch (InvocationTargetException e1) {
}
copy(installer.jre, location);
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
progress.setIndeterminate(false);
progress.setValue(1);
}
});
} catch (InterruptedException e1) {
} catch (InvocationTargetException e1) {
}
}
// Unix permissions for executable files
if (os.equals("Unix")) {
File chmod = new File("/usr/bin/chmod");
if (!chmod.exists()) chmod = new File("/bin/chmod");
if (!chmod.exists()) chmod = new File("/usr/local/bin/chmod");
if (!chmod.exists()) chmod = new File("/sbin/chmod");
if (!chmod.exists()) chmod = new File("/usr/sbin/chmod");
if (!chmod.exists()) chmod = new File("/usr/local/sbin/chmod");
if (chmod.exists()) {
String cmd = chmod.getAbsolutePath() + " +x ";
// Permission for launch script => mandatory package, always present
File jsynoptic = new File(location,"jsynoptic.sh");
if (jsynoptic.exists()) Runtime.getRuntime().exec(cmd + jsynoptic.getAbsolutePath());
File jreBinDir = new File(new File(location, "jre"),"bin");
if (jreBinDir.exists()) {
File[] binaries = jreBinDir.listFiles();
for (int i=0; i<binaries.length; ++i) {
Runtime.getRuntime().exec(cmd + binaries[i].getAbsolutePath());
}
}
// Also make the JSynoptic run script executable
Runtime.getRuntime().exec(cmd + "jsynoptic.sh");
}
}
} catch (IOException e) {
installer.previous.setEnabled(true);
installer.next.setEnabled(true);
tfLoc.setEnabled(true);
btnLoc.setEnabled(true);
JOptionPane.showMessageDialog(
installer,
Installer.resources.getString("installErrorIs")+e.getLocalizedMessage(),
Installer.resources.getString("errorInInstall"),
JOptionPane.ERROR_MESSAGE
);
synchronized(this) {
installFinished = false;
installThread = null;
}
return;
}
synchronized(this) {
installFinished = true;
progress.setString(Installer.resources.getString("installComplete"));
installer.next.setText(Installer.resources.getString("run"));
installer.cancel.setText(Installer.resources.getString("dontRun"));
ActionListener[] listeners = installer.cancel.getActionListeners();
for (int i=0; i<listeners.length; ++i) installer.cancel.removeActionListener(listeners[i]);
installer.cancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
installer.writeProperties();
System.exit(0);
}
});
installer.next.setEnabled(true);
}
}
/**
* Recursive copy of file1 to file2, including subdirectories
* @param src A file or directory to copy recursively
* @param dest The name of the destination file if src is actually a file. In case dest is a directory, src will be copied in this directory with the same name.
* @throws IOException if an error occurs, and in particular if src or dest refers to a non existent file or directory
*/
protected void copy(File src, File dest) throws IOException {
if (src.isDirectory() && dest.isFile()) throw new IOException("Cannot copy a directory to a file");
if (src.isDirectory()) {
// so, location is a direcory too
// Create an empty dir
File newDir = new File(dest, src.getName());
if (!newDir.mkdirs()) throw new IOException("Cannot create a new Directory");
// recursively copy all entries in the new dir
File[] entries = src.listFiles();
for (int i=0; i<entries.length; ++i) copy(entries[i],newDir);
return;
}
// So, f is a file
if (dest.isDirectory()) {
// Create a new file of that name in the empty directory
File newFile = new File(dest, src.getName());
newFile.createNewFile();
// Use copy in the last case : file to file
copy(src,newFile);
return;
}
try {
// file to file transfer
if (src.length()==0) {
dest.createNewFile();
return;
}
// transfering >0 bytes
FileChannel fc = new FileInputStream(src).getChannel();
FileChannel dstChannel = new FileOutputStream(dest).getChannel();
long transfered = 0;
long totalLength = src.length();
while (transfered < totalLength) {
long num = fc.transferTo(transfered,totalLength-transfered,dstChannel);
if (num==0) throw new IOException("Error while copying");
transfered += num;
}
dstChannel.close();
fc.close();
} catch (IOException e) {
if (os.equals("Unix")) {
_logger.fine("Trying to use cp to copy file...");
File cp = new File("/usr/bin/cp");
if (!cp.exists()) cp = new File("/bin/cp");
if (!cp.exists()) cp = new File("/usr/local/bin/cp");
if (!cp.exists()) cp = new File("/sbin/cp");
if (!cp.exists()) cp = new File("/usr/sbin/cp");
if (!cp.exists()) cp = new File("/usr/local/sbin/cp");
if (cp.exists()) {
Process cpProcess = Runtime.getRuntime().exec(cp.getAbsolutePath() + " '" + src.getAbsolutePath() + "' '"+dest.getAbsolutePath()+"'");
int errCode;
try {
errCode = cpProcess.waitFor();
} catch (java.lang.InterruptedException ie) {
throw e;
}
return;
}
}
throw e;
}
}
protected class BarUpdater implements Runnable {
int delta;
public BarUpdater(int delta) {
this.delta = delta;
}
public void run() {
progress.setValue(progress.getValue()+delta);
}
}
protected void updateLoc() {
try {
tfLoc.setText(location.getCanonicalPath());
} catch (IOException e) {
tfLoc.setText(location.getAbsolutePath());
}
}
public void update() {
synchronized(this) {
if (installFinished) {
installer.next.setText(Installer.resources.getString("run"));
installer.cancel.setText(Installer.resources.getString("dontRun"));
installer.previous.setEnabled(false);
}
else {
installer.next.setText(Installer.resources.getString("install"));
installer.cancel.setText(Installer.resources.getString("cancel"));
}
}
setBorder(BorderFactory.createTitledBorder(Installer.resources.getString("selectPackages")));
// update all packages with current lang
for (int i=1; i<=numPackages; ++i) {
JCheckBox cb = (JCheckBox)boxes.get(i-1);
String name;
try {
name = Installer.resources.getString("packageName"+i+os);
} catch (MissingResourceException e1) {
name = Installer.resources.getString("packageName"+i);
}
cb.setText(name);
}
tipPane.setText(Installer.resources.getString("pointPackageForDescription"));
progress.setMinimum(0);
progress.setMaximum(1);
progress.setValue(0);
progress.setStringPainted(false);
}
protected class TipListener extends MouseAdapter {
protected String key;
// store key, not text, because lang may change => force get text from resources each time
public TipListener(String resourceKey) {
key = resourceKey;
}
public void mouseEntered(MouseEvent e) {
String text;
try {
text = Installer.resources.getString(key+os);
} catch (MissingResourceException e1) {
text = Installer.resources.getString(key);
}
tipPane.setText(text);
JScrollBar sb = tipScroll.getVerticalScrollBar();
if (sb!=null) sb.setValue(sb.getMinimum());
}
}
}