summaryrefslogtreecommitdiff
path: root/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/BKULauncher.java
diff options
context:
space:
mode:
Diffstat (limited to 'BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/BKULauncher.java')
-rw-r--r--BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/BKULauncher.java339
1 files changed, 241 insertions, 98 deletions
diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/BKULauncher.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/BKULauncher.java
index 854e6535..3d09fb00 100644
--- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/BKULauncher.java
+++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/BKULauncher.java
@@ -1,15 +1,23 @@
package at.gv.egiz.bku.webstart;
+import iaik.asn1.CodingException;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.MalformedURLException;
import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -25,16 +33,157 @@ import org.apache.commons.logging.LogFactory;
import at.gv.egiz.bku.webstart.ui.BKUControllerInterface;
import at.gv.egiz.bku.webstart.ui.TrayIconDialog;
import at.gv.egiz.bku.utils.StreamUtil;
+import java.awt.Desktop;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.net.URI;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.util.UUID;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.zip.ZipOutputStream;
public class BKULauncher implements BKUControllerInterface {
+ /** configurations with less than this (major) version will be backuped and updated */
+ public static final String MIN_CONFIG_VERSION = "1.0.3";
+ public static final String CONFIG_DIR = ".mocca/conf/";
+ public static final String CONF_TEMPLATE_FILE = "template.zip";
+ public static final String CONF_TEMPLATE_RESOURCE = "at/gv/egiz/bku/webstart/conf/template.zip";
+ public static final String WEBAPP_RESOURCE = "BKULocal.war";
+ public static final String WEBAPP_FILE = "BKULocal.war";
+ public static final String KEYSTORE_FILE = "keystore.ks";
+ public static final String MESSAGES_RESOURCE = "at/gv/egiz/bku/webstart/ui/UIMessages";
+ public static final String PASSWD_FILE = ".secret";
+ /** resource bundle messages */
+ public static final String GREETING_CAPTION = "Greetings.Caption";
+ public static final String GREETING_MESSAGE = "Greetings.Message";
+ public static final String VERSION_FILE = ".version";
private static Log log = LogFactory.getLog(BKULauncher.class);
-
private ResourceBundle resourceBundle = null;
private Container server;
-// private SplashScreen splash = SplashScreen.getSplashScreen();
+ private void createConfig(File configDir, File versionFile, String version) throws IOException, CertificateException, GeneralSecurityException, KeyStoreException, FileNotFoundException, NoSuchAlgorithmException {
+ log.debug("creating config directory: " + configDir);
+ configDir.mkdirs();
+ InputStream is = getClass().getClassLoader().getResourceAsStream(CONF_TEMPLATE_RESOURCE);
+ OutputStream os = new FileOutputStream(new File(configDir, CONF_TEMPLATE_FILE));
+ StreamUtil.copyStream(is, os);
+ os.close();
+ File confTemplateFile = new File(configDir, CONF_TEMPLATE_FILE);
+ unzip(confTemplateFile);
+ confTemplateFile.delete();
+ writeVersionFile(versionFile, version);
+ }
+
+ private void createCertificates(File configDir) throws IOException, GeneralSecurityException, CodingException {
+ char[] password = UUID.randomUUID().toString().toCharArray();
+ File passwdFile = new File(configDir, PASSWD_FILE);
+ FileWriter passwdWriter = new FileWriter(passwdFile);
+ passwdWriter.write(password);
+ passwdWriter.close();
+ if (!passwdFile.setReadable(true, true)) {
+ passwdFile.delete();
+ throw new IOException("failed to make " + passwdFile + " owner readable only, deleting file");
+ }
+ TLSServerCA ca = new TLSServerCA();
+ KeyStore ks = ca.generateKeyStore(password);
+ FileOutputStream fos = new FileOutputStream(new File(configDir, KEYSTORE_FILE));
+ ks.store(fos, password);
+ fos.close();
+ }
+
+ private String getFileVersion(File versionFile) throws FileNotFoundException, IOException {
+ //TODO no file?
+ if (versionFile.exists() && versionFile.canRead()) {
+ BufferedReader versionReader = new BufferedReader(new FileReader(versionFile));
+ String versionString = null;
+ while ((versionString = versionReader.readLine().trim()) != null) {
+ if (versionString.length() > 0 && !versionString.startsWith("#")) {
+ log.debug("found existing configuration version " + versionString);
+ break;
+ }
+ }
+ return versionString;
+ }
+ return null;
+ }
+
+ private String getManifestVersion() throws MalformedURLException, IOException {
+ String bkuWebStartJar = BKULauncher.class.getProtectionDomain().getCodeSource().getLocation().toString();
+ URL manifestURL = new URL("jar:" + bkuWebStartJar + "!/META-INF/MANIFEST.MF");
+ String version = null;
+ if (manifestURL != null) {
+ Manifest manifest = new Manifest(manifestURL.openStream());
+ if (log.isTraceEnabled()) {
+ log.trace("read version information from " + manifestURL);
+ }
+ Attributes atts = manifest.getMainAttributes();
+ if (atts != null) {
+ version = atts.getValue("Implementation-Build");
+ }
+ }
+ if (version == null) {
+ version = "UNKNOWN";
+ }
+ log.debug("config version: " + version);
+ return version;
+ }
+
+ /**
+ * change the
+ * @param oldVersion
+ * @param newVersion
+ * @return
+ */
+ private boolean updateRequired(String oldVersion, String newVersion) {
+ if (oldVersion != null) {
+ int majorEnd = oldVersion.indexOf('-');
+ if (majorEnd > 0) {
+ oldVersion = oldVersion.substring(0, majorEnd);
+ }
+ return (oldVersion.compareTo(MIN_CONFIG_VERSION) < 0);
+ }
+ log.debug("no old version, update required");
+ return true;
+ }
+
+ private boolean updateRequiredStrict(String oldVersion, String newVersion) {
+ String[] oldV = oldVersion.split("-");
+ String[] newV = newVersion.split("-");
+ log.debug("comparing " + oldV[0] + " to " + newV[0]);
+ if (oldV[0].compareTo(newV[0]) < 0) {
+ log.debug("update required");
+ return true;
+ } else {
+ log.debug("comparing " + oldV[oldV.length - 1] + " to " + newV[newV.length - 1]);
+ if (oldV[oldV.length - 1].compareTo(newV[newV.length - 1]) < 0) {
+ log.debug("update required");
+ return true;
+ } else {
+ log.debug("no update required");
+ return false;
+ }
+ }
+ }
+
+ private void writeVersionFile(File versionFile, String version) throws IOException {
+ BufferedWriter versionWriter = new BufferedWriter(new FileWriter(versionFile));
+ versionWriter.write("# MOCCA Web Start configuration version\n");
+ versionWriter.write("# DO NOT MODIFY THIS FILE\n\n");
+ versionWriter.write(version);
+ versionWriter.close();
+ }
+
+// private SplashScreen splash = SplashScreen.getSplashScreen();
private void startUpServer() throws Exception {
+ log.info("init servlet container and MOCCA webapp");
server = new Container();
// XmlConfiguration xcfg = new XmlConfiguration(getClass().getClassLoader()
// .getResourceAsStream("at/gv/egiz/bku/local/app/jetty.xml"));
@@ -44,25 +193,24 @@ public class BKULauncher implements BKUControllerInterface {
}
private void initTrayIcon() {
+ log.debug("init MOCCA tray icon");
Locale loc = Locale.getDefault();
try {
resourceBundle = ResourceBundle.getBundle(
- "at/gv/egiz/bku/webstart/ui/UIMessages", loc);
+ MESSAGES_RESOURCE, loc);
} catch (MissingResourceException mx) {
resourceBundle = ResourceBundle.getBundle(
- "at/gv/egiz/bku/webstart/ui/UIMessages", Locale.ENGLISH);
+ MESSAGES_RESOURCE, Locale.ENGLISH);
}
TrayIconDialog.getInstance().init(resourceBundle);
TrayIconDialog.getInstance().setShutdownHook(this);
- TrayIconDialog.getInstance().displayInfo("Greetings.Caption",
- "Greetings.Message");
+ TrayIconDialog.getInstance().displayInfo(GREETING_CAPTION, GREETING_MESSAGE);
}
private void initStart() {
-
}
- private void initFinished() {
+ private void initFinished(boolean installCert) {
try {
// if (splash != null) {
// try {
@@ -71,34 +219,33 @@ public class BKULauncher implements BKUControllerInterface {
// log.warn("Failed to close splash screen: " + ex.getMessage());
// }
// }
+
+ log.debug("trying install MOCCA certificate on system browser");
+ if (installCert) {
+ if (Desktop.isDesktopSupported()) {
+ Desktop desktop = Desktop.getDesktop();
+ if (desktop.isSupported(Desktop.Action.BROWSE)) {
+ try {
+ desktop.browse(new URI("https://localhost:" +
+ Integer.getInteger(Container.HTTPS_PORT_PROPERTY, 3496).intValue()));
+ } catch (Exception ex) {
+ log.error("failed to open system browser, install MOCCA certificate manually", ex);
+ }
+ } else {
+ log.error("failed to open system browser, install MOCCA certificate manually");
+ }
+ } else {
+ log.error("failed to open system browser, install MOCCA certificate manually");
+ }
+ }
+
+ log.info("init completed, joining server");
server.join();
} catch (InterruptedException e) {
- log.info(e);
+ log.warn("failed to join MOCCA server: " + e.getMessage(), e);
}
}
-// private void copyDirs(File srcDir, File dstDir) {
-// for (File cf : srcDir.listFiles()) {
-// File of = new File(dstDir, cf.getName());
-// if (cf.isDirectory()) {
-// log.debug("Creating directory: " + of);
-// of.mkdir();
-// copyDirs(cf, of);
-// } else {
-// log.debug("Writing file: " + of);
-// try {
-// FileInputStream fis = new FileInputStream(cf);
-// FileOutputStream fos = new FileOutputStream(of);
-// StreamUtil.copyStream(fis, fos);
-// fis.close();
-// fos.close();
-// } catch (IOException e) {
-// log.error("Cannot copy default configuration", e);
-// }
-// }
-// }
-// }
-
private void unzip(File zipfile) throws IOException {
File dir = zipfile.getParentFile();
ZipFile zipFile = new ZipFile(zipfile);
@@ -113,73 +260,11 @@ public class BKULauncher implements BKUControllerInterface {
File f = new File(eF.getParent());
f.mkdirs();
StreamUtil.copyStream(zipFile.getInputStream(entry),
- new FileOutputStream(eF));
+ new FileOutputStream(eF));
}
zipFile.close();
}
- private void checkConfig(String[] args) {
-// CommandLineParser parser = new PosixParser();
-// Options options = new Options();
-// options.addOption("c", true, "the configuration's base directory");
-// options.addOption("h", false, "print this message");
-// try {
- File cfgDir = new File(System.getProperty("user.home") + "/.mocca/conf");
-// CommandLine cmd = parser.parse(options, args);
-// if (cmd.hasOption("h")) {
-// HelpFormatter formatter = new HelpFormatter();
-// formatter.printHelp("BKULauncher", options);
-// System.exit(0);
-// }
-//
-// if (cmd.hasOption("c")) {
-// cfgDir = new File(cmd.getOptionValue("c"));
-// }
- log.debug("using config directory: " + cfgDir);
- if (cfgDir.exists() && cfgDir.isFile()) {
- log.error("Configuration directory must not be a file");
- }
- if (!cfgDir.exists()) {
- log.debug("Creating config directory: " + cfgDir);
- cfgDir.mkdirs();
- try {
- InputStream is = getClass().getClassLoader().getResourceAsStream(
- "at/gv/egiz/bku/webstart/defaultConf/template.zip");
- OutputStream os = new FileOutputStream(new File(cfgDir,
- "template.zip"));
- StreamUtil.copyStream(is, os);
- os.close();
- unzip(new File(cfgDir, "template.zip"));
- } catch (IOException iox) {
- log.error("Cannot create user directory", iox);
- return;
- }
- CA ca = new CA();
- char[] password = "changeMe".toCharArray();
- KeyStore ks = ca.generateKeyStore(password);
- if (ks != null) {
- File ksdir = new File(cfgDir, "keystore");
- ksdir.mkdirs();
- FileOutputStream fos;
- try {
- fos = new FileOutputStream(new File(ksdir, "keystore.ks"));
- ks.store(fos, password);
- fos.close();
- } catch (Exception e) {
- log.error("Cannot store keystore", e);
- }
- } else {
- log.error("Cannot create ssl certificate");
- }
- }
-// } catch (ParseException e1) {
-// log.error(e1);
-// HelpFormatter formatter = new HelpFormatter();
-// formatter.printHelp("BKULauncher", options);
-// System.exit(0);
-// }
- }
-
/**
* @param args
*/
@@ -189,15 +274,74 @@ public class BKULauncher implements BKUControllerInterface {
System.setSecurityManager(null);
BKULauncher launcher = new BKULauncher();
launcher.initStart();
- launcher.checkConfig(args);
+
+ File configDir = new File(System.getProperty("user.home") + '/' + CONFIG_DIR);
+ boolean installCert = launcher.ensureConfig(configDir);
launcher.startUpServer();
launcher.initTrayIcon();
- launcher.initFinished();
+ launcher.initFinished(installCert);
} catch (Exception e) {
- log.fatal("Cannot launch BKU", e);
+ log.fatal("Failed to launch BKU: " + e.getMessage(), e);
System.exit(-1000);
}
+ }
+
+ private void backupAndDelete(File dir, URI relativeTo, ZipOutputStream zip) throws IOException {
+ if (dir.isDirectory()) {
+ File[] subDirs = dir.listFiles();
+ for (File subDir : subDirs) {
+ backupAndDelete(subDir, relativeTo, zip);
+ subDir.delete();
+ }
+ } else {
+ URI relativePath = relativeTo.relativize(dir.toURI());
+ ZipEntry entry = new ZipEntry(relativePath.toString());
+ zip.putNextEntry(entry);
+ BufferedInputStream entryIS = new BufferedInputStream(new FileInputStream(dir));
+ StreamUtil.copyStream(entryIS, zip);
+ entryIS.close();
+ zip.closeEntry();
+ dir.delete();
+ }
+ }
+ /**
+ * Checks whether the config directory already exists and creates it otherwise.
+ * @param configDir the config directory to be created
+ * @return true if a new MOCCA cert was created (and needs to be installed in the browser)
+ */
+ private boolean ensureConfig(File configDir) throws IOException, GeneralSecurityException, CodingException {
+ log.debug("config directory: " + configDir);
+ String manifestVersion = getManifestVersion();
+ File versionFile = new File(configDir, VERSION_FILE);
+
+ if (configDir.exists()) {
+ if (configDir.isFile()) {
+ log.error("invalid config directory: " + configDir);
+ throw new IOException("invalid config directory: " + configDir);
+ } else {
+ String fileVersion = getFileVersion(versionFile);
+ if (updateRequired(fileVersion, manifestVersion)) {
+ if (fileVersion == null) {
+ fileVersion = "unknown";
+ }
+ log.info("updating configuration from " + fileVersion + " to " + manifestVersion);
+ File moccaDir = configDir.getParentFile();
+ File zipFile = new File(moccaDir, "conf-" + fileVersion + ".zip");
+ ZipOutputStream zipOS = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
+ backupAndDelete(configDir, moccaDir.toURI(), zipOS);
+ zipOS.close();
+ createConfig(configDir, versionFile, manifestVersion);
+ createCertificates(configDir);
+ return true;
+ }
+ }
+ } else {
+ createConfig(configDir, versionFile, manifestVersion);
+ createCertificates(configDir);
+ return true;
+ }
+ return false;
}
public void shutDown() {
@@ -217,5 +361,4 @@ public class BKULauncher implements BKUControllerInterface {
}
System.exit(0);
}
-
}