Fix for PR#118484 - added support for the OSGi Preferences Service
diff --git a/bundles/org.eclipse.equinox.preferences/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.preferences/META-INF/MANIFEST.MF
index ac1f9ed..8a4c2a2 100644
--- a/bundles/org.eclipse.equinox.preferences/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.preferences/META-INF/MANIFEST.MF
@@ -11,6 +11,6 @@
Export-Package: org.eclipse.core.internal.preferences;x-friends:="org.eclipse.core.resources,org.eclipse.core.runtime",
org.eclipse.core.runtime,
org.eclipse.core.runtime.preferences,
- org.osgi.service.prefs;version="1.0"
+ org.osgi.service.prefs;version="1.1"
Eclipse-LazyStart: true
Import-Package: org.eclipse.equinox.registry
diff --git a/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/Activator.java b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/Activator.java
index 1735ddd..f69e53d 100644
--- a/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/Activator.java
+++ b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/Activator.java
@@ -21,6 +21,8 @@
*/
public class Activator implements BundleActivator {
+ private static final String OSGI_PREFERENCES_SERVICE = "org.osgi.service.prefs.PreferencesService"; //$NON-NLS-1$
+
/**
* The bundle associated this plug-in
*/
@@ -32,6 +34,11 @@
private ServiceRegistration preferencesService = null;
/**
+ * This plugin provides the OSGi Preferences service.
+ */
+ private ServiceRegistration osgiPreferencesService;
+
+ /**
* This method is called upon plug-in activation
*/
public void start(BundleContext context) throws Exception {
@@ -55,10 +62,13 @@
private void registerServices() {
preferencesService = bundleContext.registerService(IPreferencesService.class.getName(), PreferencesService.getDefault(), new Hashtable());
+ osgiPreferencesService = bundleContext.registerService(OSGI_PREFERENCES_SERVICE, new OSGiPreferencesServiceManager(bundleContext), null);
}
private void unregisterServices() {
preferencesService.unregister();
+ osgiPreferencesService.unregister();
+
}
/**
diff --git a/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/OSGiPreferencesServiceImpl.java b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/OSGiPreferencesServiceImpl.java
new file mode 100644
index 0000000..8a13cc0
--- /dev/null
+++ b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/OSGiPreferencesServiceImpl.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.preferences;
+
+import java.io.File;
+import java.util.*;
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.osgi.service.prefs.*;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+
+/**
+ * <p>
+ * Implements OSGi PreferencesService using the Eclipse preference system.
+ * </p>
+ *
+ * <p>
+ * Note: Eclipse preferences are not accessible through the OSGi Preferences API and vice
+ * versa.
+ * </p>
+ */
+public class OSGiPreferencesServiceImpl implements PreferencesService {
+
+ /**
+ * Adaptor that implements OSGi Preferences interface on top of EclipsePreferences.
+ *
+ */
+ private static final class OSGiPreferences extends EclipsePreferences implements Preferences {
+
+ private IPath location;
+ private IEclipsePreferences loadLevel;
+ private OSGiPreferencesServiceImpl prefsServiceImpl;
+
+ private OSGiPreferences(File prefsDir, OSGiPreferencesServiceImpl prefsServiceImpl) {
+ super(null, ""); //$NON-NLS-1$
+ this.prefsServiceImpl = prefsServiceImpl;
+ this.location = new Path(prefsDir.getPath());
+ this.loadLevel = this;
+ }
+
+ private OSGiPreferences(EclipsePreferences nodeParent, String nodeName, OSGiPreferencesServiceImpl prefsServiceImpl) {
+ super(nodeParent, nodeName);
+ this.loadLevel = nodeParent.getLoadLevel();
+ this.prefsServiceImpl = prefsServiceImpl;
+ }
+
+ protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) {
+ return new OSGiPreferences(nodeParent, nodeName, prefsServiceImpl);
+ }
+
+ protected IPath getLocation() {
+ return location;
+ }
+
+ protected IEclipsePreferences getLoadLevel() {
+ return loadLevel;
+ }
+
+ /**
+ * Override node(String pathName) to be more strict about forbidden names -
+ * EclipsePreferences implementation does a best-effort instead of throwing
+ * {@link IllegalArgumentException}.
+ */
+ public Preferences node(String pathName) {
+ if ((pathName.length() > 1 && pathName.endsWith("/")) //$NON-NLS-1$
+ || pathName.indexOf("//") != -1) { //$NON-NLS-1$
+ throw new IllegalArgumentException();
+ }
+ return super.node(pathName);
+ }
+
+ /**
+ * Override removeNode() to allow removal of root nodes. EclipsePreferences ignores
+ * attempts to remove the root node, but in OSGi Preferences there are many root nodes
+ * and removal is permitted.
+ */
+ public void removeNode() throws BackingStoreException {
+ if (parent() == null) {
+ flush();
+ if (this == prefsServiceImpl.systemPreferences) {
+ prefsServiceImpl.systemPreferences = null;
+ } else {
+ prefsServiceImpl.userPreferences.values().remove(this);
+ }
+ }
+
+ super.removeNode();
+ removed = true;
+ }
+
+ /**
+ * <p>
+ * Override getByteArray(String key, byte [] defaultValue) to be more strict when
+ * decoding byte values. EclipsePreferences implementation pads bytes if they are not 4
+ * bytes long, but the OSGi TCK expects this function to return null if the length of
+ * the byte array is not an even multiple of 4.
+ * </p>
+ * <p>
+ * Also catches any decoding exceptions and returns the default value instead of
+ * propagating the exception.
+ * </p>
+ */
+ public byte[] getByteArray(String key, byte[] defaultValue) {
+ String value = internalGet(key);
+ byte[] byteArray = null;
+ if (value != null) {
+ byte[] encodedBytes = value.getBytes();
+ if (encodedBytes.length % 4 == 0) {
+ try {
+ byteArray = Base64.decode(encodedBytes);
+ } catch (Exception e) {
+ //do not raise exception - return defaultValue
+ }
+ }
+ }
+ return byteArray == null ? defaultValue : byteArray;
+ }
+
+ }
+
+ private File systemPrefsDir;
+ private File userPrefsDir;
+
+ Preferences systemPreferences;
+
+ //Map of String user name -> Preferences
+ Map userPreferences;
+
+ OSGiPreferencesServiceImpl(File prefsLocation) {
+ systemPrefsDir = new File(prefsLocation, "system"); //$NON-NLS-1$
+ userPrefsDir = new File(prefsLocation, "user"); //$NON-NLS-1$
+ userPreferences = new TreeMap(); //use TreeMap since keys are strings
+ }
+
+ public Preferences getSystemPreferences() {
+ if (systemPreferences == null) {
+ systemPreferences = new OSGiPreferences(systemPrefsDir, this);
+ try {
+ systemPreferences.sync();
+ } catch (BackingStoreException e) {
+ //nothing
+ }
+ }
+ return systemPreferences;
+ }
+
+ public Preferences getUserPreferences(String name) {
+ Preferences userPref = (Preferences) userPreferences.get(name);
+ if (userPref == null) {
+ userPref = new OSGiPreferences(new File(userPrefsDir, name), this);
+ try {
+ userPref.sync();
+ } catch (BackingStoreException e) {
+ //nothing
+ }
+ userPreferences.put(name, userPref);
+ }
+ return userPref;
+ }
+
+ public String[] getUsers() {
+ return userPrefsDir.list();
+ }
+
+ /**
+ * Called when Bundle ungets Preferences Service - flushes all preferences to disk.
+ */
+ void destroy() {
+ try {
+ if (systemPreferences != null && systemPreferences.nodeExists("")) { //$NON-NLS-1$
+ systemPreferences.flush();
+ }
+ } catch (BackingStoreException e) {
+ //nothing
+ }
+ Iterator it = userPreferences.values().iterator();
+ while (it.hasNext()) {
+ Preferences userPreference = (Preferences) it.next();
+ try {
+ if (userPreference.nodeExists("")) { //$NON-NLS-1$
+ userPreference.flush();
+ }
+ } catch (BackingStoreException e) {
+ //nothing
+ }
+ }
+
+ }
+}
diff --git a/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/OSGiPreferencesServiceManager.java b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/OSGiPreferencesServiceManager.java
new file mode 100644
index 0000000..770e86c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/OSGiPreferencesServiceManager.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.internal.preferences;
+
+import java.io.File;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.osgi.framework.*;
+
+/**
+ * <p>
+ * Class used to manage OSGi Preferences Service. Creates a new OSGiPreferencesServiceImpl
+ * object for every bundle that gets the Preferences Service. When a bundle ungets the
+ * Preference Service, it's preferences are flushed to disk.
+ * </p>
+ * <p>
+ * Also deletes saved preferences for bundles which are uninstalled.
+ * </p>
+ * <p>
+ * Preferences are saved in the Bundle Data area under the directory "OSGiPreferences".
+ * </p>
+ */
+public class OSGiPreferencesServiceManager implements ServiceFactory, BundleListener {
+
+ private static final String OSGI_PREFS_DIR = "OSGiPreferences"; //$NON-NLS-1$
+
+ private File prefsDir;
+
+ public OSGiPreferencesServiceManager(BundleContext context) {
+
+ prefsDir = context.getDataFile(OSGI_PREFS_DIR);
+
+ context.addBundleListener(this);
+
+ //clean up prefs for bundles that have been uninstalled
+ Bundle[] allBundles = context.getBundles();
+ Set bundleDirNames = new TreeSet();
+ for (int i = 0; i < allBundles.length; i++) {
+ bundleDirNames.add(getBundleDirName(allBundles[i]));
+ }
+ File[] prefsNodeDirs = prefsDir.listFiles();
+ prefsNodeDirs = prefsNodeDirs == null ? new File[0] : prefsNodeDirs;
+
+ for (int i = 0; i < prefsNodeDirs.length; i++) {
+ if (!bundleDirNames.contains(prefsNodeDirs[i].getName())) {
+ rmdir(prefsNodeDirs[i]);
+ }
+
+ }
+ }
+
+ /**
+ * Recursively remove a file or a directory and all of it's children.
+ */
+ private void rmdir(File file) {
+ if (!file.exists()) {
+ return;
+ }
+ if (file.isDirectory()) {
+ File[] children = file.listFiles();
+
+ for (int i = 0; i < children.length; i++) {
+ rmdir(children[i]);
+ }
+ }
+ file.delete();
+ }
+
+ /**
+ * Bundle Preferences are saves in a directory with the same name as the bundle's
+ * symbolic id. For backwards compatibility, preferences for bundles that do not
+ * have a symbolic id are saved in a directory named
+ * 'org.eclipse.core.internal.preferences.OSGiPreferences.bundleid.<bundle id>'.
+ */
+ private String getBundleDirName(Bundle bundle) {
+ String bundleDirName = bundle.getSymbolicName();
+
+ //backwards compatibility - if bundle does not have symbolic name
+ if (bundleDirName == null) {
+ bundleDirName = "org.eclipse.core.internal.preferences.OSGiPreferences.bundleid." + bundle.getBundleId(); //$NON-NLS-1$
+ }
+ return bundleDirName;
+ }
+
+ /**
+ * Creates a new OSGiPreferencesServiceImpl for each bundle.
+ */
+ public Object getService(Bundle bundle, ServiceRegistration registration) {
+ return new OSGiPreferencesServiceImpl(new File(prefsDir, getBundleDirName(bundle)));
+ }
+
+ /**
+ * Flush the bundle's preferences to disk.
+ */
+ public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+ ((OSGiPreferencesServiceImpl) service).destroy();
+ }
+
+ /**
+ * If a bundle is uninstalled, delete all of it's preferences from the disk.
+ */
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.UNINSTALLED) {
+ File bundlePrefs = new File(prefsDir, getBundleDirName(event.getBundle()));
+ rmdir(bundlePrefs);
+ }
+
+ }
+
+}