changeset 2288:f4f7b88ae02c

Merge
author andrew
date Mon, 11 Oct 2010 21:06:13 +0100
parents 420a4eede08d (current diff) 4698e476b886 (diff)
children 2c5c2c6f314a
files ChangeLog NEWS
diffstat 8 files changed, 229 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Oct 11 21:05:13 2010 +0100
+++ b/ChangeLog	Mon Oct 11 21:06:13 2010 +0100
@@ -14,6 +14,26 @@
 	* patches/security/20101012/6990437.patch:
 	Added.
 
+2010-10-08  Omair Majid  <omajid@redhat.com>
+
+	* NEWS: Updated
+	* net/sourceforge/jnlp/Launcher.java: Add fileLock.
+	(launchApplication): Call markNetxRunning and install a shutdown hook for
+	markNetxStopped.
+	(markNetxRunning): New method.
+	(markNetxStopped): New method.
+	* net/sourceforge/jnlp/cache/CacheUtil.java
+	(R): New method.
+	(clearCache): New method.
+	(okToClearCache): New method.
+	* net/sourceforge/jnlp/resources/Messages.properties: Add BXclearcache and
+	CCannotClearCache.
+	* net/sourceforge/jnlp/runtime/Boot.java
+	(run): Clear the cache.
+	* net/sourceforge/jnlp/runtime/JNLPRuntime.java: Add NETX_RUNNING_FILE.
+	* net/sourceforge/jnlp/util/FileUtils.java
+	(recursiveDelete): New method.
+
 2010-10-05  Andrew John Hughes  <ahughes@redhat.com>
 
 	* patches/security/20100330/hotspot/original/6626217.patch:
--- a/NEWS	Mon Oct 11 21:05:13 2010 +0100
+++ b/NEWS	Mon Oct 11 21:06:13 2010 +0100
@@ -38,6 +38,7 @@
   - S6650759: Inference of formal type parameter (unused in formal parameters) is not performed
 * Netx
   - A new man page for javaws.
+  - Add a new option -Xclearcache
 * Plugin  
   - PR554: System.err writes content two times
   - PR556: Applet initialization code is prone to race conditions
--- a/netx/net/sourceforge/jnlp/Launcher.java	Mon Oct 11 21:05:13 2010 +0100
+++ b/netx/net/sourceforge/jnlp/Launcher.java	Mon Oct 11 21:06:13 2010 +0100
@@ -20,12 +20,17 @@
 import java.applet.Applet;
 import java.awt.Container;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadMXBean;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
 import java.net.URL;
 import java.net.UnknownHostException;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.jar.JarFile;
@@ -78,6 +83,9 @@
     /** If the application should call System.exit on fatal errors */
     private boolean exitOnFailure = true;
 
+    /** a lock which is held to indicate that an instance of netx is running */
+    private FileLock fileLock;
+
     /**
      * Create a launcher with the runtime's default update policy
      * and launch handler.
@@ -127,6 +135,7 @@
 
         this.handler = handler;
         this.updatePolicy = policy;
+
     }
 
     /**
@@ -385,6 +394,11 @@
         if (!file.isApplication())
             throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplication"), R("LNotApplicationInfo")));
 
+        markNetxRunning();
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            public void run() { markNetxStopped(); }
+         });
+
         try {
 
             try {
@@ -686,6 +700,69 @@
         return null; // chose to continue, or no handler
     }
 
+    /**
+     * Indicate that netx is running by creating the {@link JNLPRuntime#INSTANCE_FILE} and
+     * acquiring a shared lock on it
+     */
+    private void markNetxRunning() {
+        try {
+            String message = "This file is used to check if netx is running";
+
+            File netxRunningFile = new File(JNLPRuntime.NETX_RUNNING_FILE);
+            netxRunningFile.getParentFile().mkdirs();
+            if (netxRunningFile.createNewFile()) {
+                FileOutputStream fos = new FileOutputStream(netxRunningFile);
+                try {
+                    fos.write(message.getBytes());
+                } finally {
+                    fos.close();
+                }
+            }
+
+            if (!netxRunningFile.isFile()) {
+                if (JNLPRuntime.isDebug()) {
+                    System.err.println("Unable to create instance file");
+                }
+                fileLock = null;
+                return;
+            }
+
+            FileInputStream is = new FileInputStream(netxRunningFile);
+            FileChannel channel = is.getChannel();
+            fileLock = channel.tryLock(0, Long.MAX_VALUE, true);
+            if (fileLock != null && fileLock.isShared()) {
+                if (JNLPRuntime.isDebug()) {
+                    System.out.println("Acquired shared lock on " +
+                            JNLPRuntime.NETX_RUNNING_FILE + " to indicate javaws is running");
+                }
+            } else {
+                fileLock = null;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    /**
+     * Indicate that netx is stopped by releasing the shared lock on
+     * {@link JNLPRuntime#INSTANCE_FILE}.
+     */
+    private void markNetxStopped() {
+        if (fileLock == null) {
+            return;
+        }
+        try {
+            fileLock.release();
+            fileLock.channel().close();
+            fileLock = null;
+            if (JNLPRuntime.isDebug()) {
+                System.out.println("Release shared lock on " + JNLPRuntime.NETX_RUNNING_FILE);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
 
 
     /**
--- a/netx/net/sourceforge/jnlp/cache/CacheUtil.java	Mon Oct 11 21:05:13 2010 +0100
+++ b/netx/net/sourceforge/jnlp/cache/CacheUtil.java	Mon Oct 11 21:06:13 2010 +0100
@@ -19,6 +19,7 @@
 
 import java.io.*;
 import java.net.*;
+import java.nio.channels.FileChannel;
 import java.util.*;
 import java.lang.reflect.*;
 import java.security.*;
@@ -37,6 +38,10 @@
  */
 public class CacheUtil {
 
+    private static String R(String key) {
+        return JNLPRuntime.getMessage(key);
+    }
+
     private static String R(String key, Object param) {
         return JNLPRuntime.getMessage(key, new Object[] {param});
     }
@@ -129,6 +134,72 @@
     }
 
     /**
+     * Clears the cache by deleting all the Netx cache files
+     *
+     * Note: Because of how our caching system works, deleting jars of another javaws
+     * process is using them can be quite disasterous. Hence why Launcher creates lock files
+     * and we check for those by calling {@link #okToClearCache()}
+     */
+    public static void clearCache() {
+
+        if (!okToClearCache()) {
+            System.err.println(R("CCannotClearCache"));
+            return;
+        }
+
+        File cacheDir = new File(JNLPRuntime.getBaseDir() + File.separator + "cache");
+        if (!(cacheDir.isDirectory())) {
+            return;
+        }
+
+        if (JNLPRuntime.isDebug()) {
+            System.err.println("Clearing cache directory: " + cacheDir);
+        }
+        try {
+            FileUtils.recursiveDelete(cacheDir, JNLPRuntime.getBaseDir());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns a boolean indicating if it ok to clear the netx application cache at this point
+     * @return true if the cache can be cleared at this time without problems
+     */
+    private static boolean okToClearCache() {
+        File otherJavawsRunning = new File(JNLPRuntime.NETX_RUNNING_FILE);
+        try {
+            if (otherJavawsRunning.isFile()) {
+                FileOutputStream fis = new FileOutputStream(otherJavawsRunning);
+                try {
+                    FileChannel channel = fis.getChannel();
+                    if (channel.tryLock() == null) {
+                        if (JNLPRuntime.isDebug()) {
+                            System.out.println("Other instances of netx are running");
+                        }
+                        return false;
+                    }
+
+                    if (JNLPRuntime.isDebug()) {
+                        System.out.println("No other instances of netx are running");
+                    }
+                    return true;
+
+                } finally {
+                    fis.close();
+                }
+            } else {
+                if (JNLPRuntime.isDebug()) {
+                    System.out.println("No instance file found");
+                }
+                return true;
+            }
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /**
      * Returns whether there is a version of the URL contents in the
      * cache and it is up to date.  This method may not return
      * immediately.
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties	Mon Oct 11 21:05:13 2010 +0100
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties	Mon Oct 11 21:06:13 2010 +0100
@@ -139,6 +139,7 @@
 BOViewer    = Shows the trusted certificate viewer.
 BOUmask     = Sets the umask for files created by an application.
 BXnofork    = Do not create another JVM.
+BXclearcache= Clean the JNLP application cache.
 BOHelp      = Print this message and exit.
 
 # Cache
@@ -149,6 +150,7 @@
 CChooseCache=Choose a cache directory...
 CChooseCacheInfo=Netx needs a location for storing cache files.
 CChooseCacheDir=Cache directory
+CCannotClearCache=Can not clear cache at this time
 
 # Security
 SFileReadAccess=The application has requested read access to {0}. Do you want to allow this action?
--- a/netx/net/sourceforge/jnlp/runtime/Boot.java	Mon Oct 11 21:05:13 2010 +0100
+++ b/netx/net/sourceforge/jnlp/runtime/Boot.java	Mon Oct 11 21:06:13 2010 +0100
@@ -40,6 +40,7 @@
 import net.sourceforge.jnlp.ParseException;
 import net.sourceforge.jnlp.PropertyDesc;
 import net.sourceforge.jnlp.ResourcesDesc;
+import net.sourceforge.jnlp.cache.CacheUtil;
 import net.sourceforge.jnlp.cache.UpdatePolicy;
 import net.sourceforge.jnlp.security.VariableX509TrustManager;
 import net.sourceforge.jnlp.security.viewer.CertificateViewer;
@@ -114,6 +115,7 @@
         + "  -strict               "+R("BOStrict")+"\n"
         + "  -umask=value          "+R("BOUmask")+"\n"
         + "  -Xnofork              "+R("BXnofork")+"\n"
+        + "  -Xclearcache          "+R("BXclearcache")+"\n"
         + "  -help                 "+R("BOHelp")+"\n";
 
     private static final String doubleArgs = "-basedir -jnlp -arg -param -property -update";
@@ -202,6 +204,17 @@
         JNLPRuntime.setSecurityEnabled(null == getOption("-nosecurity"));
         JNLPRuntime.initialize(true);
 
+        /*
+         * FIXME
+         * This should have been done with the rest of the argument parsing
+         * code. But we need to know what the cache and base directories are,
+         * and baseDir is initialized here
+         */
+        if (null != getOption("-Xclearcache")) {
+            CacheUtil.clearCache();
+            return null;
+        }
+
         try {
             new Launcher().launch(getFile());
         }
--- a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java	Mon Oct 11 21:05:13 2010 +0100
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java	Mon Oct 11 21:06:13 2010 +0100
@@ -18,6 +18,7 @@
 package net.sourceforge.jnlp.runtime;
 
 import java.io.*;
+import java.nio.channels.FileLock;
 import java.awt.*;
 import java.text.*;
 import java.util.*;
@@ -132,6 +133,15 @@
     public static final String LOCKS_DIR = TMP_DIR + File.separator + USER + File.separator
             + "netx" + File.separator + "locks";
 
+    /**
+     * The /tmp/$USER/netx/locks/netx_running file is used to indicate if any
+     * instances of netx are running (this file may exist even if no instances
+     * are running). All netx instances acquire a shared lock on this file. If
+     * this file can be locked (using a {@link FileLock}) in exclusive mode, then
+     * other netx instances are not running
+     */
+    public static final String NETX_RUNNING_FILE = LOCKS_DIR + File.separator + "netx_running";
+
     /** the java.home directory */
     public static final String JAVA_HOME_DIR = System.getProperty("java.home");
 
--- a/netx/net/sourceforge/jnlp/util/FileUtils.java	Mon Oct 11 21:05:13 2010 +0100
+++ b/netx/net/sourceforge/jnlp/util/FileUtils.java	Mon Oct 11 21:06:13 2010 +0100
@@ -17,6 +17,9 @@
 package net.sourceforge.jnlp.util;
 
 import java.io.File;
+import java.io.IOException;
+
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
 
 /**
  * This class contains a few file-related utility functions.
@@ -121,4 +124,36 @@
         return prefix + OMITTED + suffix;
     }
 
+    /**
+     * Recursively delete everything under a directory. Works on either files or
+     * directories
+     *
+     * @param file the file object representing what to delete. Can be either a
+     *        file or a directory.
+     * @param base the directory under which the file and its subdirectories must be located
+     * @throws IOException on an io exception or if trying to delete something
+     *         outside the base
+     */
+    public static void recursiveDelete(File file, File base) throws IOException {
+        if (JNLPRuntime.isDebug()) {
+            System.err.println("Deleting: " + file);
+        }
+
+        if (!(file.getCanonicalPath().startsWith(base.getCanonicalPath()))) {
+            throw new IOException("Trying to delete a file outside Netx's basedir: "
+                    + file.getCanonicalPath());
+        }
+
+        if (file.isDirectory()) {
+            File[] children = file.listFiles();
+            for (int i = 0; i < children.length; i++) {
+                recursiveDelete(children[i], base);
+            }
+        }
+        if (!file.delete()) {
+            throw new IOException("Unable to delete file: " + file);
+        }
+
+    }
+
 }