You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pk...@apache.org on 2008/08/25 15:05:30 UTC

svn commit: r688714 - in /felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall: DirectoryWatcher.java FileInstall.java

Author: pkriens
Date: Mon Aug 25 06:05:30 2008
New Revision: 688714

URL: http://svn.apache.org/viewvc?rev=688714&view=rev
Log:
FELIX-524, FELIX-529, diverse small improvements

Modified:
    felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java
    felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java

Modified: felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java?rev=688714&r1=688713&r2=688714&view=diff
==============================================================================
--- felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java (original)
+++ felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java Mon Aug 25 06:05:30 2008
@@ -29,25 +29,21 @@
 
 import org.osgi.framework.*;
 import org.osgi.service.cm.*;
+import org.osgi.service.log.*;
 import org.osgi.service.packageadmin.*;
 
-public class DirectoryWatcher extends Thread
-{
-    final static String ALIAS_KEY = "_alias_factory_pid";
-    public final static String POLL = "felix.fileinstall.poll";
-    public final static String DIR = "felix.fileinstall.dir";
-    public final static String DEBUG = "felix.fileinstall.debug";
-    File jardir;
-    boolean cont = true;
-    long poll = 2000;
-    long debug;
-    Map foundBundles = new HashMap();
-    Map foundConfigs = new HashMap();
-    BundleContext context;
-    boolean reported;
+public class DirectoryWatcher extends Thread {
+    final static String        ALIAS_KEY = "_alias_factory_pid";
+    public final static String POLL      = "felix.fileinstall.poll";
+    public final static String DIR       = "felix.fileinstall.dir";
+    public final static String DEBUG     = "felix.fileinstall.debug";
+    File                       watchedDirectory;
+    long                       poll      = 2000;
+    long                       debug;
+    BundleContext              context;
+    boolean                    reported;
 
-    public DirectoryWatcher(Dictionary properties, BundleContext context)
-    {
+    public DirectoryWatcher(Dictionary properties, BundleContext context) {
         super(properties.toString());
         this.context = context;
         poll = getLong(POLL, poll);
@@ -55,116 +51,110 @@
 
         String dir = (String) properties.get(DIR);
         if (dir == null)
-        {
             dir = "./load";
-        }
-
-        jardir = new File(dir);
-        jardir.mkdirs();
-    }
 
-    long getLong(String property, long dflt)
-    {
-        String value = context.getProperty(property);
-        if (value != null)
-        {
-            try
-            {
-                return Long.parseLong(value);
-            }
-            catch (Exception e)
-            {
-                System.out.println(property + " set, but not a long: " + value);
-            }
-        }
-        return dflt;
+        this.watchedDirectory = new File(dir);
+        this.watchedDirectory.mkdirs();
     }
 
-    public void run()
-    {
-        System.out.println(POLL + "  (ms)   " + poll);
-        System.out.println(DIR + "            " + jardir.getAbsolutePath());
-        System.out.println(DEBUG + "          " + debug);
-
-        while (cont)
-        {
-            try
-            {
-                Map installed = new HashMap();
-                Map configs = new HashMap();
-                traverse(installed, configs, jardir);
-                doInstalled(foundBundles, installed);
-                doConfigs(foundConfigs, configs);
+    /**
+     * Main run loop, will traverse the directory, and then handle the delta
+     * between installed and newly found/lost bundles and configurations.
+     * 
+     */
+    public void run() {
+        log(POLL + "  (ms)   " + poll, null);
+        log(DIR + "            " + watchedDirectory.getAbsolutePath(), null);
+        log(DEBUG + "          " + debug, null);
+        Map currentManagedBundles = new HashMap(); // location -> Long(time)
+        Map currentManagedConfigs = new HashMap(); // location -> Long(time)
+
+        while (!interrupted()) {
+            try {
+                Set/* <String> */installed = new HashSet();
+                Set/* <String> */configs = new HashSet();
+                traverse(installed, configs, watchedDirectory);
+                doInstalled(currentManagedBundles, installed);
+                doConfigs(currentManagedConfigs, configs);
                 Thread.sleep(poll);
-            }
-            catch (InterruptedException e)
-            {
-
-            }
-            catch (Throwable e)
-            {
+            } catch (InterruptedException e) {
+                return;
+            } catch (Throwable e) {
                 log("In main loop, we have serious trouble", e);
             }
         }
     }
 
-    private void doConfigs(Map old, Map newConfigs)
-    {
-        try
-        {
-            Set oldKeys = new HashSet(old.keySet());
-            for (Iterator e = newConfigs.entrySet().iterator(); e.hasNext();)
-            {
-                Map.Entry entry = (Map.Entry) e.next();
-                String path = (String) entry.getKey();
-                File f = (File) entry.getValue();
-                if (!oldKeys.contains(path))
-                {
-                    // new
+    /**
+     * Handle the changes between the configurations already installed and the
+     * newly found/lost configurations.
+     * 
+     * @param current
+     *            Existing installed configurations abspath -> File
+     * @param discovered
+     *            Newly found configurations
+     */
+    void doConfigs(Map current, Set discovered) {
+        try {
+            // Set all old keys as inactive, we remove them
+            // when we find them to be active, will be left
+            // with the inactive ones.
+            Set inactive = new HashSet(current.keySet());
+
+            for (Iterator e = discovered.iterator(); e.hasNext();) {
+                String path = (String) e.next();
+                File f = new File(path);
+
+                if (!current.containsKey(path)) {
+                    // newly found entry, set the config immedialey
                     Long l = new Long(f.lastModified());
-                    if (setConfig(f))
-                    {
-                        old.put(path, l);
+                    if (setConfig(f)) {
+                        // Remember it for the next round
+                        current.put(path, l);
                     }
-                }
-                else
-                {
+                } else {
+                    // Found an existing one.
+                    // Check if it has been updated
                     long lastModified = f.lastModified();
-                    long oldTime = ((Long) old.get(path)).longValue();
-                    if (oldTime < lastModified)
-                    {
-                        if (setConfig(f))
-                        {
-                            old.put(path, new Long(lastModified));
+                    long oldTime = ((Long) current.get(path)).longValue();
+                    if (oldTime < lastModified) {
+                        if (setConfig(f)) {
+                            // Remember it for the next round.
+                            current.put(path, new Long(lastModified));
                         }
                     }
                 }
-                oldKeys.remove(path);
+                // Mark this one as active
+                inactive.remove(path);
             }
-            for (Iterator e = oldKeys.iterator(); e.hasNext();)
-            {
+            for (Iterator e = inactive.iterator(); e.hasNext();) {
                 String path = (String) e.next();
                 File f = new File(path);
-                if (deleteConfig(f))
-                {
-                    foundConfigs.remove(path);
+                if (deleteConfig(f)) {
+                    current.remove(path);
                 }
             }
-        }
-        catch (Exception ee)
-        {
+        } catch (Exception ee) {
             log("Processing config: ", ee);
         }
     }
 
-    private boolean setConfig(File f) throws Exception
-    {
-        ConfigurationAdmin cm = (ConfigurationAdmin) FileInstall.cmTracker.getService();
-        if (cm == null)
-        {
-            if (debug != 0 && !reported)
-            {
-                System.err.println("Can't find a Configuration Manager, configurations do not work");
+    /**
+     * Set the configuration based on the config file.
+     * 
+     * @param f
+     *            Configuration file
+     * @return
+     * @throws Exception
+     */
+    boolean setConfig(File f) throws Exception {
+        ConfigurationAdmin cm = (ConfigurationAdmin) FileInstall.cmTracker
+                .getService();
+        if (cm == null) {
+            if (debug != 0 && !reported) {
+                log(
+                        "Can't find a Configuration Manager, configurations do not work",
+                        null);
                 reported = true;
             }
             return false;
@@ -177,231 +167,293 @@
         String pid[] = parsePid(f.getName());
         Hashtable ht = new Hashtable();
         ht.putAll(p);
-        if (pid[1] != null)
-        {
+        if (pid[1] != null) {
             ht.put(ALIAS_KEY, pid[1]);
         }
         Configuration config = getConfiguration(pid[0], pid[1]);
-        if (config.getBundleLocation() != null)
-        {
+        if (config.getBundleLocation() != null) {
             config.setBundleLocation(null);
         }
         config.update(ht);
         return true;
     }
 
-    private boolean deleteConfig(File f) throws Exception
-    {
+    /**
+     * Remove the configuration.
+     * 
+     * @param f
+     *            File where the configuration in whas defined.
+     * @return
+     * @throws Exception
+     */
+    boolean deleteConfig(File f) throws Exception {
         String pid[] = parsePid(f.getName());
         Configuration config = getConfiguration(pid[0], pid[1]);
         config.delete();
         return true;
     }
 
-    String[] parsePid(String path)
-    {
+    String[] parsePid(String path) {
         String pid = path.substring(0, path.length() - 4);
         int n = pid.indexOf('-');
-        if (n > 0)
-        {
+        if (n > 0) {
             String factoryPid = pid.substring(n + 1);
             pid = pid.substring(0, n);
-            return new String[]{pid, factoryPid};
-        }
-        else
-        {
-            return new String[]{pid, null};
+            return new String[] { pid, factoryPid };
+        } else {
+            return new String[] { pid, null };
         }
     }
 
-    private Configuration getConfiguration(String pid, String factoryPid)
-        throws Exception
-    {
-        ConfigurationAdmin cm = (ConfigurationAdmin) FileInstall.cmTracker.getService();
-        if (factoryPid != null)
-        {
-            Configuration configs[] = cm.listConfigurations("(|(" + ALIAS_KEY + "=" + factoryPid + ")(.alias_factory_pid=" + factoryPid + "))");
-            if (configs == null || configs.length == 0)
-            {
+    Configuration getConfiguration(String pid, String factoryPid)
+            throws Exception {
+        ConfigurationAdmin cm = (ConfigurationAdmin) FileInstall.cmTracker
+                .getService();
+        if (factoryPid != null) {
+            String filter = "(|(" + ALIAS_KEY + "=" + factoryPid
+                    + ")(.alias_factory_pid=" + factoryPid + "))";
+            Configuration configs[] = cm.listConfigurations(filter);
+            if (configs == null || configs.length == 0) {
                 return cm.createFactoryConfiguration(pid, null);
-            }
-            else
-            {
+            } else {
                 return configs[0];
             }
-        }
-        else
-        {
+        } else {
             return cm.getConfiguration(pid, null);
         }
     }
 
-    private void doInstalled(Map sizes, Map installed)
-    {
+    /**
+     * Install bundles that were discovered and uninstall bundles that are gone
+     * from the current state.
+     * 
+     * @param current
+     *            A map location -> path that holds the current state
+     * @param discovered
+     *            A set of paths that represent the just found bundles
+     */
+    void doInstalled(Map current, Set discovered) {
         boolean refresh = false;
         Bundle bundles[] = context.getBundles();
-        for (int i = 0; i < bundles.length; i++)
-        {
+        for (int i = 0; i < bundles.length; i++) {
             Bundle bundle = bundles[i];
             String location = bundle.getLocation();
-            File file = (File) installed.get(location);
-            if (file != null)
-            {
+            if (discovered.contains(location)) {
+                // We have a bundle that is already installed
+                // so we know it
+                discovered.remove(location);
+
+                File file = new File(location);
+
                 // Modified date does not work on the Nokia
                 // for some reason, so we take size into account
                 // as well.
                 long newSize = file.length();
-                Long oldSizeObj = (Long) sizes.get(location);
+                Long oldSizeObj = (Long) current.get(location);
                 long oldSize = oldSizeObj == null ? 0 : oldSizeObj.longValue();
 
-                installed.remove(location);
-                if (file.lastModified() > bundle.getLastModified() + 4000 && oldSize != newSize)
-                {
-                    try
-                    {
-                        sizes.put(location, new Long(newSize));
+                if (file.lastModified() > bundle.getLastModified() + 4000
+                        && oldSize != newSize) {
+                    try {
+                        // We treat this as an update, it is modified,,
+                        // different size, and it is present in the dir
+                        // as well as in the list of bundles.
+                        current.put(location, new Long(newSize));
                         InputStream in = new FileInputStream(file);
                         bundle.update(in);
                         refresh = true;
                         in.close();
                         log("Updated " + location, null);
-                    }
-                    catch (Exception e)
-                    {
+                    } catch (Exception e) {
                         log("Failed to update bundle ", e);
                     }
                 }
-                if (!isFragment(bundle))
-                {
-                    try
-                    {
+
+                // Fragments can not be started. All other
+                // bundles are always started because OSGi treats this
+                // as a noop when the bundle is already started
+                if (!isFragment(bundle)) {
+                    try {
                         bundle.start();
-                    }
-                    catch (Exception e)
-                    {
+                    } catch (Exception e) {
                         log("Fail to start bundle " + location, e);
                     }
                 }
-            }
-            else
-            {
-                if (bundle.getLocation().startsWith(jardir.getAbsolutePath()))
-                {
-                    try
-                    {
+            } else {
+                // Hmm. We found a bundlethat looks like it came from our
+                // watched directory but we did not find it this round.
+                // Just remove it.
+                if (bundle.getLocation().startsWith(
+                        watchedDirectory.getAbsolutePath())) {
+                    try {
                         bundle.uninstall();
                         refresh = true;
                         log("Uninstalled " + location, null);
-                    }
-                    catch (Exception e)
-                    {
+                    } catch (Exception e) {
                         log("failed to uninstall bundle: ", e);
                     }
                 }
             }
         }
 
-        for (Iterator it = installed.values().iterator(); it.hasNext();)
-        {
-            try
-            {
-                File file = (File) it.next();
+        List starters = new ArrayList();
+        for (Iterator it = discovered.iterator(); it.hasNext();) {
+            try {
+                String path = (String) it.next();
+                File file = new File(path);
                 InputStream in = new FileInputStream(file);
-                Bundle bundle = context.installBundle(file.getAbsolutePath(),
-                    in);
-                refresh = true;
+                Bundle bundle = context.installBundle(path, in);
                 in.close();
-                if (!isFragment(bundle))
-                {
-                    bundle.start();
-                }
+
+                // We do not start this bundle yet. We wait after
+                // refresh because this will minimize the disruption
+                // as well as temporary unresolved errors.
+                starters.add(bundle);
+
                 log("Installed " + file.getAbsolutePath(), null);
-            }
-            catch (Exception e)
-            {
+            } catch (Exception e) {
                 log("failed to install/start bundle: ", e);
             }
         }
-        if (refresh)
-        {
+
+        if (refresh || starters.size() != 0) {
             refresh();
+            for (Iterator b = starters.iterator(); b.hasNext();) {
+                Bundle bundle = (Bundle) b.next();
+                if (!isFragment(bundle)) {
+                    try {
+                        bundle.start();
+                    } catch (BundleException e) {
+                        log("Error while starting a newly installed bundle", e);
+                    }
+                }
+            }
         }
     }
 
-    private void log(String string, Throwable e)
-    {
-        System.err.println(string + ": " + e);
-        if (debug > 0 && e != null)
-        {
-            e.printStackTrace();
+    /**
+     * Log a message and optional throwable. If there is a log service we use
+     * it, otherwise we log to the console
+     * 
+     * @param message
+     *            The message to log
+     * @param e
+     *            The throwable to log
+     */
+    void log(String message, Throwable e) {
+        LogService log = getLogService();
+        if (log == null)
+            System.out.println(message + (e == null ? "" : ": " + e));
+        else {
+            if (e == null) {
+                log.log(LogService.LOG_ERROR, message, e);
+                if (debug > 0 && e != null) {
+                    e.printStackTrace();
+                }
+            } else
+                log.log(LogService.LOG_INFO, message);
         }
     }
 
-    private void traverse(Map jars, Map configs, File jardir2)
-    {
+    /**
+     * Answer the Log Service
+     * 
+     * @return
+     */
+    LogService getLogService() {
+        ServiceReference ref = context.getServiceReference(LogService.class
+                .getName());
+        if (ref != null) {
+            LogService log = (LogService) context.getService(ref);
+            return log;
+        }
+        return null;
+    }
+
+    /**
+     * Traverse the directory and fill the map with the found jars and
+     * configurations keyed by the abs file path.
+     * 
+     * @param jars
+     *            Returns the abspath -> file for found jars
+     * @param configs
+     *            Returns the abspath -> file for found configurations
+     * @param jardir
+     *            The directory to traverse
+     */
+    void traverse(Set jars, Set configs, File jardir) {
         String list[] = jardir.list();
-        for (int i = 0; i < list.length; i++)
-        {
-            File file = new File(jardir2, list[i]);
-            if (list[i].endsWith(".jar"))
-            {
-                jars.put(file.getAbsolutePath(), file);
-            }
-            else if (list[i].endsWith(".cfg"))
-            {
-                configs.put(file.getAbsolutePath(), file);
+        for (int i = 0; i < list.length; i++) {
+            File file = new File(jardir, list[i]);
+            if (list[i].endsWith(".jar")) {
+                jars.add(file.getAbsolutePath());
+            } else if (list[i].endsWith(".cfg")) {
+                configs.add(file.getAbsolutePath());
             }
         }
     }
 
-    private boolean isFragment(Bundle bundle)
-    {
+    /**
+     * Check if a bundle is a fragment.
+     * 
+     * @param bundle
+     * @return
+     */
+    boolean isFragment(Bundle bundle) {
         PackageAdmin padmin;
-        if (FileInstall.padmin == null)
-        {
+        if (FileInstall.padmin == null) {
             return false;
         }
 
-        try
-        {
+        try {
             padmin = (PackageAdmin) FileInstall.padmin.waitForService(10000);
-            if (padmin != null)
-            {
+            if (padmin != null) {
                 return padmin.getBundleType(bundle) == PackageAdmin.BUNDLE_TYPE_FRAGMENT;
             }
-        }
-        catch (InterruptedException e)
-        {
-        // stupid exception
+        } catch (InterruptedException e) {
+            // stupid exception
         }
         return false;
     }
 
-    private void refresh()
-    {
+    /**
+     * Convenience to refresh the packages
+     */
+    void refresh() {
         PackageAdmin padmin;
-        try
-        {
+        try {
             padmin = (PackageAdmin) FileInstall.padmin.waitForService(10000);
             padmin.refreshPackages(null);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
         }
-        catch (InterruptedException e)
-        {
-        // stupid exception
+    }
+
+    /**
+     * Answer the long from a property.
+     * 
+     * @param property
+     * @param dflt
+     * @return
+     */
+    long getLong(String property, long dflt) {
+        String value = context.getProperty(property);
+        if (value != null) {
+            try {
+                return Long.parseLong(value);
+            } catch (Exception e) {
+                log(property + " set, but not a long: " + value, null);
+            }
         }
+        return dflt;
     }
 
-    public void close()
-    {
-        cont = false;
+    public void close() {
         interrupt();
-        try
-        {
+        try {
             join(10000);
-        }
-        catch (InterruptedException ie)
-        {
-        // Ignore
+        } catch (InterruptedException ie) {
+            // Ignore
         }
     }
-}
\ No newline at end of file
+}

Modified: felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java?rev=688714&r1=688713&r2=688714&view=diff
==============================================================================
--- felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java (original)
+++ felix/trunk/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java Mon Aug 25 06:05:30 2008
@@ -16,7 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.felix.fileinstall;
 
 import java.util.*;
@@ -29,82 +28,75 @@
 /**
  * This clever little bundle watches a directory and will install any jar file
  * if finds in that directory (as long as it is a valid bundle and not a
- * fragment). Test
+ * fragment).
  * 
  */
-public class FileInstall implements BundleActivator, ManagedServiceFactory
-{
+public class FileInstall implements BundleActivator, ManagedServiceFactory {
     static ServiceTracker padmin;
     static ServiceTracker cmTracker;
-    BundleContext context;
-    Map watchers = new HashMap();
+    BundleContext         context;
+    Map                   watchers = new HashMap();
 
-    public void start(BundleContext context) throws Exception
-    {
+    public void start(BundleContext context) throws Exception {
         this.context = context;
         Hashtable props = new Hashtable();
         props.put(Constants.SERVICE_PID, getName());
-        context.registerService(ManagedServiceFactory.class.getName(), this, props);
+        context.registerService(ManagedServiceFactory.class.getName(), this,
+                props);
 
         padmin = new ServiceTracker(context, PackageAdmin.class.getName(), null);
         padmin.open();
-        cmTracker = new ServiceTracker(context, ConfigurationAdmin.class.getName(), null);
+        cmTracker = new ServiceTracker(context, ConfigurationAdmin.class
+                .getName(), null);
         cmTracker.open();
 
         // Created the initial configuration
         Hashtable ht = new Hashtable();
+
         set(ht, DirectoryWatcher.POLL);
         set(ht, DirectoryWatcher.DIR);
         set(ht, DirectoryWatcher.DEBUG);
         updated("initial", ht);
     }
 
-    private void set(Hashtable ht, String key)
-    {
+    // Adapted for FELIX-524
+    private void set(Hashtable ht, String key) {
         Object o = context.getProperty(key);
-        if (o == null)
-        {
-            return;
+        if (o == null) {
+            o = System.getenv(key.toUpperCase().replaceAll(".", "_"));
+            if (o == null)
+                return;
         }
         ht.put(key, o);
     }
 
-    public void stop(BundleContext context) throws Exception
-    {
-        for (Iterator w = watchers.values().iterator(); w.hasNext();)
-        {
-            try
-            {
+    public void stop(BundleContext context) throws Exception {
+        for (Iterator w = watchers.values().iterator(); w.hasNext();) {
+            try {
                 DirectoryWatcher dir = (DirectoryWatcher) w.next();
                 w.remove();
                 dir.close();
-            }
-            catch (Exception e)
-            {
-            // Ignore
+            } catch (Exception e) {
+                // Ignore
             }
         }
         cmTracker.close();
         padmin.close();
     }
 
-    public void deleted(String pid)
-    {
+    public void deleted(String pid) {
         DirectoryWatcher watcher = (DirectoryWatcher) watchers.remove(pid);
-        if (watcher != null)
-        {
+        if (watcher != null) {
             watcher.close();
         }
     }
 
-    public String getName()
-    {
+    public String getName() {
         return "org.apache.felix.fileinstall";
     }
 
     public void updated(String pid, Dictionary properties)
-        throws ConfigurationException
-    {
+            throws ConfigurationException {
         deleted(pid);
         DirectoryWatcher watcher = new DirectoryWatcher(properties, context);
         watchers.put(pid, watcher);