FELIX-524, FELIX-529, diverse small improvements
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@688714 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java
index 0e52021..7b39b90 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/DirectoryWatcher.java
@@ -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();
+ this.watchedDirectory = new File(dir);
+ this.watchedDirectory.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;
- }
+ /**
+ * 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)
- 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);
+ 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)
- {
- // stupid exception
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
}
}
- public void close()
- {
- cont = false;
- interrupt();
- try
- {
- join(10000);
+ /**
+ * 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);
+ }
}
- catch (InterruptedException ie)
- {
- // Ignore
+ return dflt;
+ }
+
+ public void close() {
+ interrupt();
+ try {
+ join(10000);
+ } catch (InterruptedException ie) {
+ // Ignore
}
}
-}
\ No newline at end of file
+}
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java
index 9dced1e..457436c 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/FileInstall.java
@@ -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);