You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2020/07/29 16:06:00 UTC

[sling-org-apache-sling-settings] branch master updated: SLING-9561 switch to DS 1.4 and component properties (#2)

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-settings.git


The following commit(s) were added to refs/heads/master by this push:
     new dea599a  SLING-9561 switch to DS 1.4 and component properties (#2)
dea599a is described below

commit dea599acfa1cd55674429dc2da6bd12ed5401c9f
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed Jul 29 18:05:52 2020 +0200

    SLING-9561 switch to DS 1.4 and component properties (#2)
---
 bnd.bnd                                            |   5 -
 pom.xml                                            |  53 ++-
 .../org/apache/sling/settings/impl/Activator.java  |  50 ---
 .../apache/sling/settings/impl/RunModeCommand.java |  35 +-
 .../sling/settings/impl/ServicesListener.java      | 262 ---------------
 .../settings/impl/SettingsServiceConfigurator.java |  58 ----
 .../settings/impl/SlingPropertiesPrinter.java      |  51 +--
 .../sling/settings/impl/SlingSettingsPrinter.java  |  38 +--
 .../settings/impl/SlingSettingsServiceImpl.java    | 232 ++++++-------
 .../resources/OSGI-INF/l10n/metatype.properties    |  29 --
 ...ling.settings.impl.SlingSettingsServiceImpl.xml |  28 --
 .../sling/settings/impl/RunModeImplTest.java       | 365 +++++++--------------
 .../impl/SlingSettingsServiceImplTest.java         |  10 +-
 13 files changed, 304 insertions(+), 912 deletions(-)

diff --git a/bnd.bnd b/bnd.bnd
index 8c25158..03ccd6c 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -1,14 +1,9 @@
-Bundle-Activator: org.apache.sling.settings.impl.Activator
-
 Bundle-DocURL: http://sling.apache.org/documentation/bundles/sling-settings-orgapacheslingsettings.html
 
 DynamicImport-Package:\
   org.osgi.service.cm,\
   org.apache.felix.shell
 
-Provide-Capability:\
-  osgi.service;objectClass=org.apache.sling.settings.SlingSettingsService
-
 Import-Package:\
   org.osgi.service.cm;resolution:=optional,\
   org.apache.felix.shell;resolution:=optional,\
diff --git a/pom.xml b/pom.xml
index ccd5970..ee21df2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,30 +1,31 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
 <!--
     Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
+    or more contributor license agreements. See the NOTICE file
     distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
+    regarding copyright ownership. The ASF licenses this file
     to you under the Apache License, Version 2.0 (the
     "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-    
+    with the License. You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
-    
+
     Unless required by applicable law or agreed to in writing,
     software distributed under the License is distributed on an
     "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
+    KIND, either express or implied. See the License for the
     specific language governing permissions and limitations
     under the License.
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling-bundle-parent</artifactId>
         <version>39</version>
-        <relativePath />
+        <relativePath/>
     </parent>
 
     <artifactId>org.apache.sling.settings</artifactId>
@@ -39,8 +40,8 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-settings.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-settings.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-settings.git</url>
-      <tag>HEAD</tag>
-  </scm>
+        <tag>HEAD</tag>
+    </scm>
 
     <properties>
         <site.jira.version.id>12315254</site.jira.version.id>
@@ -76,6 +77,23 @@
             <artifactId>org.osgi.annotation.versioning</artifactId>
             <scope>provided</scope>
         </dependency>
+        <!-- https://osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/component/annotations/package-frame.html -->
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!-- https://osgi.org/javadoc/osgi.cmpn/7.0.0/org/osgi/service/metatype/annotations/package-frame.html -->
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component</artifactId>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>osgi.core</artifactId>
@@ -83,7 +101,6 @@
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.service.cm</artifactId>
-            <version>1.5.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -96,7 +113,7 @@
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
-      <!-- Testing -->
+        <!-- Testing -->
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
@@ -117,5 +134,17 @@
             <version>1.10.19</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.converter</artifactId>
+            <version>1.0.14</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.util.function</artifactId>
+            <version>1.0.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/src/main/java/org/apache/sling/settings/impl/Activator.java b/src/main/java/org/apache/sling/settings/impl/Activator.java
deleted file mode 100644
index 91781f2..0000000
--- a/src/main/java/org/apache/sling/settings/impl/Activator.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.settings.impl;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-
-/**
- * This is the bundle activator.
- * It registers the SlingSettingsService.
- *
- */
-public class Activator implements BundleActivator {
-
-    /** The service listener */
-    private ServicesListener servicesListener;
-
-    /**
-     * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
-     */
-    public void start(final BundleContext bundleContext) throws Exception {
-        this.servicesListener = new ServicesListener(bundleContext);
-    }
-
-    /**
-     * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
-     */
-    public void stop(final BundleContext context) throws Exception {
-        if ( this.servicesListener != null ) {
-            this.servicesListener.deactivate();
-            this.servicesListener = null;
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/settings/impl/RunModeCommand.java b/src/main/java/org/apache/sling/settings/impl/RunModeCommand.java
index 06b0dd3..f7fd3f8 100644
--- a/src/main/java/org/apache/sling/settings/impl/RunModeCommand.java
+++ b/src/main/java/org/apache/sling/settings/impl/RunModeCommand.java
@@ -19,44 +19,36 @@
 package org.apache.sling.settings.impl;
 
 import java.io.PrintStream;
-import java.util.Dictionary;
-import java.util.Hashtable;
 import java.util.Set;
 
 import org.apache.felix.shell.Command;
+import org.apache.sling.settings.SlingSettingsService;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.propertytypes.ServiceDescription;
 
 /**
  * Run mode command for the shell.
  */
+@Component
+@ServiceDescription("Apache Sling Sling Run Mode Shell Command")
 public class RunModeCommand implements Command {
 
     private static final String CMD_NAME = "runmodes";
 
-    private final ServiceRegistration pluginReg;
-
     private final Set<String> modes;
 
-    public RunModeCommand(final BundleContext btx, final Set<String> modes) {
-        this.modes = modes;
-
-        final Dictionary<String, String> props = new Hashtable<String, String>();
-        props.put(Constants.SERVICE_DESCRIPTION,
-            "Apache Sling Sling Run Mode Shell Command");
-        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
-
-        pluginReg = btx.registerService(Command.class.getName(), this, props);
-    }
-
-    public void destroy() {
-        pluginReg.unregister();
+    @Activate
+    public RunModeCommand(final BundleContext btx, @Reference SlingSettingsService slingSettings) {
+        this.modes = slingSettings.getRunModes();
     }
 
     /**
      * @see org.apache.felix.shell.Command#getName()
      */
+    @Override
     public String getName() {
         return CMD_NAME;
     }
@@ -64,6 +56,7 @@ public class RunModeCommand implements Command {
     /**
      * @see org.apache.felix.shell.Command#getShortDescription()
      */
+    @Override
     public String getShortDescription() {
         return "lists current run modes";
     }
@@ -71,6 +64,7 @@ public class RunModeCommand implements Command {
     /**
      * @see org.apache.felix.shell.Command#getUsage()
      */
+    @Override
     public String getUsage() {
         return CMD_NAME;
     }
@@ -78,9 +72,10 @@ public class RunModeCommand implements Command {
     /**
      * @see org.apache.felix.shell.Command#execute(java.lang.String, java.io.PrintStream, java.io.PrintStream)
      */
+    @Override
     public void execute(String command, PrintStream out, PrintStream err) {
         out.print("Current Run Modes: ");
-        if (modes == null || modes.size() == 0) {
+        if (modes == null || modes.isEmpty()) {
             out.println("-");
         } else {
             out.println(modes);
diff --git a/src/main/java/org/apache/sling/settings/impl/ServicesListener.java b/src/main/java/org/apache/sling/settings/impl/ServicesListener.java
deleted file mode 100644
index 8ac0e96..0000000
--- a/src/main/java/org/apache/sling/settings/impl/ServicesListener.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.settings.impl;
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.apache.sling.settings.SlingSettingsService;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-
-/**
- * The <code>ServicesListener</code> listens for the required services
- * and registers the settings service once all required services are
- * available
- */
-public class ServicesListener {
-
-    /** The bundle context. */
-    private final BundleContext bundleContext;
-
-    /** The listener for configuration admin. */
-    private ConfigAdminListener configAdminListener;
-
-    /** The listener for the shell. */
-    private ShellListener shellListener;
-
-
-    /** The registration of the settings service. */
-    private ServiceRegistration settingsReg;
-
-    /**
-     * Start listeners
-     */
-    public ServicesListener(final BundleContext bundleContext) {
-        this.bundleContext = bundleContext;
-        final SlingSettingsServiceImpl settingsService = new SlingSettingsServiceImpl(bundleContext);
-
-        final Dictionary<String, String> props = new Hashtable<String, String>();
-        props.put(Constants.SERVICE_DESCRIPTION,
-            "Apache Sling Settings Service");
-        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
-        this.settingsReg = bundleContext.registerService(new String[] {
-                                               SlingSettingsService.class.getName()},
-                                               settingsService, props);
-        SlingPropertiesPrinter.initPlugin(bundleContext);
-        SlingSettingsPrinter.initPlugin(bundleContext, settingsService);
-
-        // add config admin support
-        this.configAdminListener = new ConfigAdminListener(settingsService);
-        this.configAdminListener.start();
-
-        // add shell support
-        this.shellListener = new ShellListener(settingsService);
-        this.shellListener.start();
-    }
-
-    /**
-     * Deactivate this listener.
-     */
-    public void deactivate() {
-        if ( this.shellListener != null ) {
-            this.shellListener.deactivate();
-            this.shellListener = null;
-        }
-        if ( this.configAdminListener != null ) {
-            this.configAdminListener.deactivate();
-            this.configAdminListener = null;
-        }
-        if ( this.settingsReg != null ) {
-            this.settingsReg.unregister();
-            this.settingsReg = null;
-        }
-
-        SlingSettingsPrinter.destroyPlugin();
-        SlingPropertiesPrinter.destroyPlugin();
-    }
-
-    /**
-     * Helper class listening for service events for a defined service.
-     */
-    private abstract class AbstractListener implements ServiceListener {
-
-        /** The name of the service. */
-        private final String serviceName;
-
-        /** The service reference. */
-        private volatile ServiceReference reference;
-
-        /** The service. */
-        private volatile Object service;
-
-        /**
-         * Constructor
-         */
-        public AbstractListener(final String serviceName) {
-            this.serviceName = serviceName;
-        }
-
-        /**
-         * Start the listener.
-         * First register a service listener and then check for the service.
-         */
-        public void start() {
-            try {
-                bundleContext.addServiceListener(this, "("
-                        + Constants.OBJECTCLASS + "=" + serviceName + ")");
-            } catch (final InvalidSyntaxException ise) {
-                // this should really never happen
-                throw new RuntimeException("Unexpected exception occurred.", ise);
-            }
-            this.retainService();
-        }
-
-        /**
-         * Unregister the listener.
-         */
-        public void deactivate() {
-            bundleContext.removeServiceListener(this);
-        }
-
-        /**
-         * Return the service (if available)
-         */
-        public synchronized Object getService() {
-            return this.service;
-        }
-
-        /**
-         * Try to get the service and notify the change.
-         */
-        private synchronized void retainService() {
-            if ( this.reference == null ) {
-                this.reference = bundleContext.getServiceReference(this.serviceName);
-                if ( this.reference != null ) {
-                    this.service = bundleContext.getService(this.reference);
-                    if ( this.service == null ) {
-                        this.reference = null;
-                    } else {
-                        serviceChanged();
-                    }
-                }
-            }
-        }
-
-        /**
-         * Try to release the service and notify the change.
-         */
-        private synchronized void releaseService() {
-            if ( this.reference != null ) {
-                this.service = null;
-                bundleContext.ungetService(this.reference);
-                this.reference = null;
-                serviceChanged();
-            }
-        }
-
-        /**
-         * @see org.osgi.framework.ServiceListener#serviceChanged(org.osgi.framework.ServiceEvent)
-         */
-        public void serviceChanged(ServiceEvent event) {
-            if (event.getType() == ServiceEvent.REGISTERED) {
-                this.retainService();
-            } else if ( event.getType() == ServiceEvent.UNREGISTERING ) {
-                this.releaseService();
-            }
-        }
-
-        protected abstract void serviceChanged();
-    }
-
-    /**
-     * Helper class listening for service events for config admin
-     */
-    private final class ConfigAdminListener extends AbstractListener {
-
-        private Object settingsServiceConfigurator;
-
-        private final SlingSettingsServiceImpl settings;
-
-        /**
-         * Constructor
-         */
-        public ConfigAdminListener(final SlingSettingsServiceImpl settings) {
-            super("org.osgi.service.cm.ConfigurationAdmin");
-            this.settings = settings;
-        }
-
-        @Override
-        protected void serviceChanged() {
-            if ( this.getService() != null && this.settingsServiceConfigurator == null ) {
-                this.settingsServiceConfigurator = new SettingsServiceConfigurator(bundleContext, settings);
-            }
-        }
-
-        @Override
-        public void deactivate() {
-            super.deactivate();
-            if ( settingsServiceConfigurator != null ) {
-                ((SettingsServiceConfigurator)settingsServiceConfigurator).destroy();
-                settingsServiceConfigurator = null;
-            }
-        }
-
-    }
-
-    /**
-     * Helper class listening for service events for config admin
-     */
-    private final class ShellListener extends AbstractListener {
-
-        private Object runModeCommand;
-
-        private final SlingSettingsServiceImpl settings;
-
-        /**
-         * Constructor
-         */
-        public ShellListener(final SlingSettingsServiceImpl settings) {
-            super("org.apache.felix.shell.ShellService");
-            this.settings = settings;
-        }
-
-        @Override
-        protected void serviceChanged() {
-            if ( this.getService() != null && this.runModeCommand == null ) {
-                this.runModeCommand = new RunModeCommand(bundleContext, settings.getRunModes());
-            }
-        }
-
-        @Override
-        public void deactivate() {
-            super.deactivate();
-            if ( runModeCommand != null ) {
-                ((RunModeCommand)runModeCommand).destroy();
-                runModeCommand = null;
-            }
-        }
-
-    }
-}
diff --git a/src/main/java/org/apache/sling/settings/impl/SettingsServiceConfigurator.java b/src/main/java/org/apache/sling/settings/impl/SettingsServiceConfigurator.java
deleted file mode 100644
index 85d0b54..0000000
--- a/src/main/java/org/apache/sling/settings/impl/SettingsServiceConfigurator.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.settings.impl;
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedService;
-
-public class SettingsServiceConfigurator implements ManagedService {
-
-    private final SlingSettingsServiceImpl settings;
-
-    private final ServiceRegistration managedServiceReg;
-
-    public SettingsServiceConfigurator(final BundleContext btx,
-            final SlingSettingsServiceImpl s) {
-        this.settings = s;
-        // setup manager service for configuration handling
-        final Dictionary<String, String> msProps = new Hashtable<String, String>();
-        msProps.put(Constants.SERVICE_PID, s.getClass().getName());
-        msProps.put(Constants.SERVICE_DESCRIPTION,
-            "Apache Sling Managed Service for the Settings Service");
-        msProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
-        managedServiceReg = btx.registerService(ManagedService.class.getName(), this, msProps);
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public void updated(final Dictionary properties) throws ConfigurationException {
-        if ( properties != null ) {
-            this.settings.update(properties);
-        }
-    }
-
-    public void destroy() {
-        managedServiceReg.unregister();
-    }
-}
diff --git a/src/main/java/org/apache/sling/settings/impl/SlingPropertiesPrinter.java b/src/main/java/org/apache/sling/settings/impl/SlingPropertiesPrinter.java
index 6ad1bab..3864e05 100644
--- a/src/main/java/org/apache/sling/settings/impl/SlingPropertiesPrinter.java
+++ b/src/main/java/org/apache/sling/settings/impl/SlingPropertiesPrinter.java
@@ -23,39 +23,29 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.net.URL;
-import java.util.Dictionary;
-import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Properties;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceRegistration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
 
 /**
  * This is a configuration printer for the web console which
  * prints out the Sling properties from Launchpad if available.
- *
  */
+@Component(service = SlingPropertiesPrinter.class, property= {"felix.webconsole.label=slingprops","felix.webconsole.title=Sling Properties","felix.webconsole.configprinter.modes=always"})
 public class SlingPropertiesPrinter {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(SlingPropertiesPrinter.class);
-
-    private static ServiceRegistration propertiesPlugin;
-
-    public static void initPlugin(final BundleContext bundleContext) {
+    @Activate
+    public SlingPropertiesPrinter(BundleContext bundleContext) throws IOException {
         // if the properties are available, we register the sling properties plugin
         final String propUrl = bundleContext.getProperty("sling.properties.url");
         if ( propUrl != null ) {
             // try to read properties
-            Properties props = null;
-            try {
-                final URL url = new URL(propUrl);
-                final InputStream is = url.openStream();
+            try (final InputStream is = new URL(propUrl).openStream()) {
                 final Properties tmp = new Properties();
                 tmp.load(is);
                 // update props
@@ -68,29 +58,10 @@ public class SlingPropertiesPrinter {
                 props = tmp;
 
             } catch (IOException ioe) {
-                LOGGER.warn("Unable to read sling properties from " + propUrl, ioe);
-            }
-            if ( props != null ) {
-                final SlingPropertiesPrinter propertiesPrinter = new SlingPropertiesPrinter(props);
-                final Dictionary<String, String> serviceProps = new Hashtable<String, String>();
-                serviceProps.put(Constants.SERVICE_DESCRIPTION,
-                    "Apache Sling Sling Properties Configuration Printer");
-                serviceProps.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
-                serviceProps.put("felix.webconsole.label", "slingprops");
-                serviceProps.put("felix.webconsole.title", "Sling Properties");
-                serviceProps.put("felix.webconsole.configprinter.modes", "always");
-
-                propertiesPlugin = bundleContext.registerService(SlingPropertiesPrinter.class.getName(),
-                        propertiesPrinter,
-                        serviceProps);
+                throw new IOException("Unable to read sling properties from " + propUrl, ioe);
             }
-        }
-    }
-
-    public static void destroyPlugin() {
-        if ( propertiesPlugin != null) {
-            propertiesPlugin.unregister();
-            propertiesPlugin = null;
+        } else {
+           throw new IllegalStateException("No bundle context property 'sling.properties.url' provided");
         }
     }
 
@@ -98,10 +69,6 @@ public class SlingPropertiesPrinter {
 
     private final Properties props;
 
-    public SlingPropertiesPrinter(final Properties props) {
-        this.props = props;
-    }
-
     /**
      * Print out the servlet filter chains.
      * @see org.apache.felix.webconsole.ConfigurationPrinter#printConfiguration(java.io.PrintWriter)
diff --git a/src/main/java/org/apache/sling/settings/impl/SlingSettingsPrinter.java b/src/main/java/org/apache/sling/settings/impl/SlingSettingsPrinter.java
index 0c1ad50..4999ff0 100644
--- a/src/main/java/org/apache/sling/settings/impl/SlingSettingsPrinter.java
+++ b/src/main/java/org/apache/sling/settings/impl/SlingSettingsPrinter.java
@@ -19,52 +19,26 @@
 package org.apache.sling.settings.impl;
 
 import java.io.PrintWriter;
-import java.util.Dictionary;
-import java.util.Hashtable;
 
 import org.apache.sling.settings.SlingSettingsService;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
 
 /**
  * This is a configuration printer for the web console which
  * prints out the sling settings.
  *
  */
+@Component(service = SlingSettingsPrinter.class, property= {"felix.webconsole.label=slingsettings","felix.webconsole.title=Sling Settings","felix.webconsole.configprinter.modes=always"})
 public class SlingSettingsPrinter {
 
-    private static ServiceRegistration pluginReg;
-
-    public static void initPlugin(final BundleContext bundleContext,
-            final SlingSettingsService service) {
-        final SlingSettingsPrinter printer = new SlingSettingsPrinter(service);
-
-        final Dictionary<String, String> props = new Hashtable<String, String>();
-        props.put(Constants.SERVICE_DESCRIPTION,
-            "Apache Sling Sling Settings Configuration Printer");
-        props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
-        props.put("felix.webconsole.label", "slingsettings");
-        props.put("felix.webconsole.title", "Sling Settings");
-        props.put("felix.webconsole.configprinter.modes", "always");
-
-        pluginReg = bundleContext.registerService(SlingSettingsPrinter.class.getName(),
-                printer,
-                props);
-    }
-
-    public static void destroyPlugin() {
-        if ( pluginReg != null) {
-            pluginReg.unregister();
-            pluginReg = null;
-        }
-    }
-
     private static String HEADLINE = "Apache Sling Settings";
 
     private final SlingSettingsService settings;
 
-    public SlingSettingsPrinter(final SlingSettingsService settings) {
+    @Activate
+    public SlingSettingsPrinter(@Reference final SlingSettingsService settings) {
         this.settings = settings;
     }
 
diff --git a/src/main/java/org/apache/sling/settings/impl/SlingSettingsServiceImpl.java b/src/main/java/org/apache/sling/settings/impl/SlingSettingsServiceImpl.java
index 16ae4b3..8b5cc89 100644
--- a/src/main/java/org/apache/sling/settings/impl/SlingSettingsServiceImpl.java
+++ b/src/main/java/org/apache/sling/settings/impl/SlingSettingsServiceImpl.java
@@ -30,30 +30,29 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
 
 import org.apache.sling.settings.SlingSettingsService;
 import org.osgi.framework.BundleContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.propertytypes.ServiceDescription;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * This is the basic implementation of the sling settings service.
- */
+/** This is the basic implementation of the sling settings service. */
+@Component
+@Designate(ocd = SlingSettingsServiceImpl.Configuration.class)
+@ServiceDescription("Apache Sling Settings Service")
 public class SlingSettingsServiceImpl
-    implements SlingSettingsService {
-
-    /** Property containing the sling name. */
-    private static final String SLING_NAME = "sling.name";
-
-    /** Property containing the sling description. */
-    private static final String SLING_DESCRIPTION = "sling.description";
+        implements SlingSettingsService {
 
     /** The logger */
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -67,40 +66,51 @@ public class SlingSettingsServiceImpl
     /** The sling home url */
     private URL slingHomeUrl;
 
-    /** The set of run modes .*/
+    /** The set of run modes . */
     private Set<String> runModes;
 
+    private Configuration configuration;
+
     /** The name of the data file holding the sling id. */
     private static final String ID_FILE = "sling.id.file";
 
     /** The name of the data file holding install run mode options */
     private static final String OPTIONS_FILE = "sling.options.file";
 
-    /** The properties for name, description. */
-    private final Map<String, String> slingProps = new HashMap<String, String>();
+    @ObjectClassDefinition(id = "org.apache.sling.settings.impl.SlingSettingsServiceImpl", name = "Apache Sling Settings Service", 
+            description = "The settings service manages some basic settings of Sling like run modes or information about the current instance.")
+    static @interface Configuration {
+        @AttributeDefinition(name = "Instance Name", description = "A human readable name for the current instance.")
+        String sling_name();
 
-    /**
-     * Create the service and search the Sling home urls and
-     * get/create a sling id.
-     * Setup run modes
-     * @param context The bundle context
-     */
-    public SlingSettingsServiceImpl(final BundleContext context) {
-        this.setupSlingProps(context);
+        @AttributeDefinition(name = "Instance Description", description = "A human readable description for the current instance.")
+        String sling_description();
+    }
+
+    /** Create the service and search the Sling home urls and get/create a sling id. Setup run modes
+     * 
+     * @param context The bundle context */
+    @Activate
+    public SlingSettingsServiceImpl(final Configuration configuration, final BundleContext context) {
+        this.configuration = configuration;
         this.setupSlingHome(context);
         this.setupSlingId(context);
-
         this.setupRunModes(context);
-
     }
-
+   
     /**
-     * Get sling home and sling home URL
+     * Constructor only to be used from tests
+     * @param runModes
      */
+    public SlingSettingsServiceImpl(String runModes) {
+        this.runModes = parseRunModes(runModes);
+    }
+
+    /** Get sling home and sling home URL */
     private void setupSlingHome(final BundleContext context) {
         this.slingHome = context.getProperty(SLING_HOME);
         final String url = context.getProperty(SLING_HOME_URL);
-        if ( url != null ) {
+        if (url != null) {
             try {
                 this.slingHomeUrl = new URL(url);
             } catch (MalformedURLException e) {
@@ -109,13 +119,11 @@ public class SlingSettingsServiceImpl
         }
     }
 
-    /**
-     * Get / create sling id
-     */
+    /** Get / create sling id */
     private void setupSlingId(final BundleContext context) {
         // try to read the id from the id file first
         final File idFile = context.getDataFile(ID_FILE);
-        if ( idFile == null ) {
+        if (idFile == null) {
             // the osgi framework does not support storing something in the file system
             throw new RuntimeException("Unable to read from bundle data file.");
         }
@@ -139,45 +147,31 @@ public class SlingSettingsServiceImpl
         }
     }
 
-    /**
-     * Get / create sling id
-     */
-    private void setupSlingProps(final BundleContext context) {
-        synchronized ( this.slingProps ) {
-            if ( this.slingProps.get(SLING_NAME) == null && context.getProperty(SLING_NAME) != null ) {
-                this.slingProps.put(SLING_NAME, context.getProperty(SLING_NAME));
-            }
-            if ( this.slingProps.get(SLING_DESCRIPTION) == null && context.getProperty(SLING_DESCRIPTION) != null ) {
-                this.slingProps.put(SLING_DESCRIPTION, context.getProperty(SLING_DESCRIPTION));
-            }
-        }
-    }
-
     static final class Options implements Serializable {
         private static final long serialVersionUID = 1L;
         String[] modes;
-        String   selected;
+        String selected;
     }
 
     private List<Options> handleOptions(final Set<String> modesSet, final String propOptions) {
         final List<Options> optionsList = new ArrayList<Options>();
-        if ( propOptions != null && propOptions.trim().length() > 0 ) {
+        if (propOptions != null && propOptions.trim().length() > 0) {
 
             final String[] options = propOptions.trim().split("\\|");
-            for(final String opt : options) {
+            for (final String opt : options) {
                 String selected = null;
                 final String[] modes = opt.trim().split(",");
-                for(int i=0; i<modes.length; i++) {
+                for (int i = 0; i < modes.length; i++) {
                     modes[i] = modes[i].trim();
-                    if ( selected != null ) {
+                    if (selected != null) {
                         modesSet.remove(modes[i]);
                     } else {
-                        if ( modesSet.contains(modes[i]) ) {
+                        if (modesSet.contains(modes[i])) {
                             selected = modes[i];
                         }
                     }
                 }
-                if ( selected == null ) {
+                if (selected == null) {
                     selected = modes[0];
                     modesSet.add(modes[0]);
                 }
@@ -190,30 +184,36 @@ public class SlingSettingsServiceImpl
         return optionsList;
     }
 
-    /**
-     * Set up run modes.
-     */
+    private Set<String> parseRunModes(String runModes) {
+        final Set<String> modesSet = new HashSet<>();
+        final String[] modes = runModes.split(",");
+        for (int i = 0; i < modes.length; i++) {
+            modesSet.add(modes[i].trim());
+        }
+        return modesSet;
+    }
+
+    /** Set up run modes. */
     private void setupRunModes(final BundleContext context) {
-        final Set<String> modesSet = new HashSet<String>();
+        final Set<String> modesSet;
 
         // check configuration property first
         final String prop = context.getProperty(RUN_MODES_PROPERTY);
         if (prop != null && prop.trim().length() > 0) {
-            final String[] modes = prop.split(",");
-            for(int i=0; i < modes.length; i++) {
-                modesSet.add(modes[i].trim());
-            }
+            modesSet = parseRunModes(prop);
+        } else {
+            modesSet = new HashSet<>();
         }
 
-        //  handle configured options
+        // handle configured options
         this.handleOptions(modesSet, context.getProperty(RUN_MODE_OPTIONS));
 
         // handle configured install options
         // read persisted options if restart or update
         final List<Options> storedOptions = readOptions(context);
-        if ( storedOptions != null ) {
-            for(final Options o : storedOptions) {
-                for(final String m : o.modes) {
+        if (storedOptions != null) {
+            for (final Options o : storedOptions) {
+                for (final String m : o.modes) {
                     modesSet.remove(m);
                 }
                 modesSet.add(o.selected);
@@ -228,19 +228,18 @@ public class SlingSettingsServiceImpl
         // make the set unmodifiable and synced
         // we probably don't need a synced set as it is read only
         this.runModes = Collections.synchronizedSet(Collections.unmodifiableSet(modesSet));
-        if ( this.runModes.size() > 0 ) {
+        if (this.runModes.size() > 0) {
             logger.info("Active run modes: {}", this.runModes);
         } else {
             logger.info("No run modes active");
         }
     }
 
-
     @SuppressWarnings("unchecked")
     private List<Options> readOptions(final BundleContext context) {
         List<Options> optionsList = null;
         final File file = context.getDataFile(OPTIONS_FILE);
-        if ( file.exists() ) {
+        if (file.exists()) {
             FileInputStream fis = null;
             ObjectInputStream ois = null;
             try {
@@ -248,16 +247,22 @@ public class SlingSettingsServiceImpl
                 ois = new ObjectInputStream(fis);
 
                 optionsList = (List<Options>) ois.readObject();
-            } catch ( final IOException ioe ) {
+            } catch (final IOException ioe) {
                 throw new RuntimeException("Unable to read from options data file.", ioe);
             } catch (ClassNotFoundException cnfe) {
                 throw new RuntimeException("Unable to read from options data file.", cnfe);
             } finally {
-                if ( ois != null ) {
-                    try { ois.close(); } catch ( final IOException ignore) {}
+                if (ois != null) {
+                    try {
+                        ois.close();
+                    } catch (final IOException ignore) {
+                    }
                 }
-                if ( fis != null ) {
-                    try { fis.close(); } catch ( final IOException ignore) {}
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    } catch (final IOException ignore) {
+                    }
                 }
             }
         }
@@ -272,53 +277,49 @@ public class SlingSettingsServiceImpl
             fos = new FileOutputStream(file);
             oos = new ObjectOutputStream(fos);
             oos.writeObject(optionsList);
-        } catch ( final IOException ioe ) {
+        } catch (final IOException ioe) {
             throw new RuntimeException("Unable to write to options data file.", ioe);
         } finally {
-            if ( oos != null ) {
-                try { oos.close(); } catch ( final IOException ignore) {}
+            if (oos != null) {
+                try {
+                    oos.close();
+                } catch (final IOException ignore) {
+                }
             }
-            if ( fos != null ) {
-                try { fos.close(); } catch ( final IOException ignore) {}
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (final IOException ignore) {
+                }
             }
         }
     }
 
-    /**
-     * @see org.apache.sling.settings.SlingSettingsService#getAbsolutePathWithinSlingHome(String)
-     */
+    /** @see org.apache.sling.settings.SlingSettingsService#getAbsolutePathWithinSlingHome(String) */
     @Override
     public String getAbsolutePathWithinSlingHome(final String relativePath) {
         return new File(slingHome, relativePath).getAbsolutePath();
     }
 
-    /**
-     * @see org.apache.sling.settings.SlingSettingsService#getSlingId()
-     */
+    /** @see org.apache.sling.settings.SlingSettingsService#getSlingId() */
     @Override
     public String getSlingId() {
         return this.slingId;
     }
 
-    /**
-     * @see org.apache.sling.settings.SlingSettingsService#getSlingHome()
-     */
+    /** @see org.apache.sling.settings.SlingSettingsService#getSlingHome() */
     @Override
     public URL getSlingHome() {
         return this.slingHomeUrl;
     }
 
-    /**
-     * @see org.apache.sling.settings.SlingSettingsService#getSlingHomePath()
-     */
+    /** @see org.apache.sling.settings.SlingSettingsService#getSlingHomePath() */
     @Override
     public String getSlingHomePath() {
         return this.slingHome;
     }
 
-    /**
-     * @see org.apache.sling.settings.SlingSettingsService#getRunModes()
-     */
+    /** @see org.apache.sling.settings.SlingSettingsService#getRunModes() */
     @Override
     public Set<String> getRunModes() {
         return this.runModes;
@@ -365,42 +366,27 @@ public class SlingSettingsServiceImpl
      */
     @Override
     public String getSlingName() {
-        synchronized ( this.slingProps ) {
-            String name = this.slingProps.get(SLING_NAME);
-            if ( name == null ) {
-                name = "Instance " + this.slingId; // default
-            }
-            return name;
+        String name = configuration.sling_name();
+        if ( name == null ) {
+            name = "Instance " + this.slingId; // default
         }
+        return name;
     }
 
-    /**
-     * @see org.apache.sling.settings.SlingSettingsService#getSlingDescription()
-     */
+    /** @see org.apache.sling.settings.SlingSettingsService#getSlingDescription() */
     @Override
     public String getSlingDescription() {
-        synchronized ( this.slingProps ) {
-            String desc = this.slingProps.get(SLING_DESCRIPTION);
-            if ( desc == null ) {
-                desc = "Instance with id " + this.slingId + " and run modes " + this.getRunModes(); // default
-            }
-            return desc;
+        String desc = configuration.sling_description();
+        if (desc == null) {
+            desc = "Instance with id " + this.slingId + " and run modes " + this.getRunModes(); // default
         }
+        return desc;
     }
 
-    /**
-     * Update the configuration of this service
-     */
-    public void update(final Dictionary<String, Object> properties) {
-        if ( properties != null ) {
-            synchronized ( this.slingProps ) {
-                if ( properties.get(SLING_NAME) != null ) {
-                    this.slingProps.put(SLING_NAME, properties.get(SLING_NAME).toString());
-                }
-                if ( properties.get(SLING_DESCRIPTION) != null ) {
-                    this.slingProps.put(SLING_DESCRIPTION, properties.get(SLING_DESCRIPTION).toString());
-                }
-            }
-        }
+    /** Update the configuration of this service */
+    @Modified
+    public void update(final Configuration configuration) {
+        // TODO is configuration thread safe i.e. new object per call?
+        this.configuration = configuration;
     }
 }
diff --git a/src/main/resources/OSGI-INF/l10n/metatype.properties b/src/main/resources/OSGI-INF/l10n/metatype.properties
deleted file mode 100644
index 8b9371f..0000000
--- a/src/main/resources/OSGI-INF/l10n/metatype.properties
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-#  Licensed to the Apache Software Foundation (ASF) under one
-#  or more contributor license agreements.  See the NOTICE file
-#  distributed with this work for additional information
-#  regarding copyright ownership.  The ASF licenses this file
-#  to you under the Apache License, Version 2.0 (the
-#  "License"); you may not use this file except in compliance
-#  with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing,
-#  software distributed under the License is distributed on an
-#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#  KIND, either express or implied.  See the License for the
-#  specific language governing permissions and limitations
-#  under the License.
-#
-
-
-settings.name = Apache Sling Settings Service
-settings.description = The settings service manages some basic settings of Sling \
- like run modes or information about the current instance.
-
-sling.name.name = Instance Name
-sling.name.description = A human readable name for the current instance.
-
-sling.description.name = Instance Description
-sling.description.description = A human readable description for the current instance.
\ No newline at end of file
diff --git a/src/main/resources/OSGI-INF/metatype/org.apache.sling.settings.impl.SlingSettingsServiceImpl.xml b/src/main/resources/OSGI-INF/metatype/org.apache.sling.settings.impl.SlingSettingsServiceImpl.xml
deleted file mode 100644
index ede0b76..0000000
--- a/src/main/resources/OSGI-INF/metatype/org.apache.sling.settings.impl.SlingSettingsServiceImpl.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-    Licensed to the Apache Software Foundation (ASF) under one
-    or more contributor license agreements.  See the NOTICE file
-    distributed with this work for additional information
-    regarding copyright ownership.  The ASF licenses this file
-    to you under the Apache License, Version 2.0 (the
-    "License"); you may not use this file except in compliance
-    with the License.  You may obtain a copy of the License at
-    
-    http://www.apache.org/licenses/LICENSE-2.0
-    
-    Unless required by applicable law or agreed to in writing,
-    software distributed under the License is distributed on an
-    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-    KIND, either express or implied.  See the License for the
-    specific language governing permissions and limitations
-    under the License.
---><metatype:MetaData xmlns:metatype="http://www.osgi.org/xmlns/metatype/v1.0.0" 
-                      localization="OSGI-INF/l10n/metatype">
-    <OCD id="org.apache.sling.settings.impl.SlingSettingsServiceImpl" name="%settings.name" description="%settings.description">
-        <AD id="sling.name" type="String" name="%sling.name.name" description="%sling.name.description"/>
-        <AD id="sling.description" type="String" name="%sling.description.name" description="%sling.description.description"/>
-    </OCD>
-    <Designate pid="org.apache.sling.settings.impl.SlingSettingsServiceImpl">
-        <Object ocdref="org.apache.sling.settings.impl.SlingSettingsServiceImpl"/>
-    </Designate>
-</metatype:MetaData>
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/settings/impl/RunModeImplTest.java b/src/test/java/org/apache/sling/settings/impl/RunModeImplTest.java
index 0d0fbe0..28b521c 100644
--- a/src/test/java/org/apache/sling/settings/impl/RunModeImplTest.java
+++ b/src/test/java/org/apache/sling/settings/impl/RunModeImplTest.java
@@ -18,39 +18,88 @@
  */
 package org.apache.sling.settings.impl;
 
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collection;
-import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
 import org.apache.sling.settings.SlingSettingsService;
-import org.osgi.framework.Bundle;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.BundleListener;
-import org.osgi.framework.Filter;
-import org.osgi.framework.FrameworkListener;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceFactory;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceObjects;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.converter.Converter;
+import org.osgi.util.converter.Converters;
 
+@RunWith(MockitoJUnitRunner.class)
 public class RunModeImplTest {
 
-    private void assertParse(String str, String [] expected) {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock(str, null, null));
+    private SlingSettingsServiceImpl.Configuration configuration;
+
+    @Mock
+    BundleContext mockBundleContext;
+
+    private Map<String, File> files = new HashMap<String, File>();
+
+    private String runModes;
+    private String options;
+    private String installOptions;
+
+    @Before
+    public void before() {
+        Converter c = Converters.standardConverter();
+        // use standard configuration
+        configuration = c.convert(new HashMap<String, Object>()).to(SlingSettingsServiceImpl.Configuration.class);
+        runModes = null;
+        options = null;
+        installOptions = null;
+        Mockito.when(mockBundleContext.getDataFile(Mockito.anyString())).then(new Answer<File>() {
+
+            @Override
+            public File answer(InvocationOnMock invocation) throws Throwable {
+                String filename = invocation.getArgumentAt(0, String.class);
+                File f = files.get(filename);
+                if ( f == null ) {
+                    try {
+                        f = File.createTempFile(filename, "id");
+                        f.delete();
+                        files.put(filename, f);
+                    } catch (IOException ioe) {
+                        throw new RuntimeException(ioe);
+                    }
+                }
+                return f;
+            }
+        });
+ 
+        Mockito.when(mockBundleContext.getProperty(Mockito.anyString())).then(new Answer<String>() {
+            @Override
+            public String answer(InvocationOnMock invocation) throws Throwable {
+                String key = invocation.getArgumentAt(0, String.class);
+                if ( key.equals(SlingSettingsService.RUN_MODES_PROPERTY) ) {
+                    return runModes;
+                } else if ( key.equals(SlingSettingsService.RUN_MODE_OPTIONS) ) {
+                    return options;
+                } else if ( key.equals(SlingSettingsService.RUN_MODE_INSTALL_OPTIONS) ) {
+                    return installOptions;
+                }
+                return null;
+            }
+         });
+    }
+
+    private void assertParse(String str, String[] expected) {
+        final SlingSettingsService rm = createSlingSettingsService(str, null, null);
         final Set<String> modes = rm.getRunModes();
 
         Set<String> expectedSet = new HashSet<String>(expected.length);
@@ -58,19 +107,20 @@ public class RunModeImplTest {
             expectedSet.add(expectedEntry);
         }
 
-        assertThat("Parsed runModes match for '" + str + "'", modes, equalTo(expectedSet));
+        Assert.assertEquals("Parsed runModes match for '" + str + "'", expectedSet, modes);
     }
 
-    @org.junit.Test public void testParseRunModes() {
+    @org.junit.Test
+    public void testParseRunModes() {
         assertParse(null, new String[0]);
         assertParse("", new String[0]);
         assertParse(" foo \t", new String[] { "foo" });
         assertParse(" foo \t,  bar\n", new String[] { "foo", "bar" });
     }
 
-    private void assertActive(SlingSettingsService s, boolean active, String ...modes) {
-        for(String mode : modes) {
-            if(active) {
+    private void assertActive(SlingSettingsService s, boolean active, String... modes) {
+        for (String mode : modes) {
+            if (active) {
                 assertTrue(mode + " should be active", s.getRunModes().contains(mode));
             } else {
                 assertFalse(mode + " should NOT be active", s.getRunModes().contains(mode));
@@ -78,286 +128,101 @@ public class RunModeImplTest {
         }
     }
 
-    @org.junit.Test public void testMatchesNotEmpty() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar", null, null));
+    @org.junit.Test
+    public void testMatchesNotEmpty() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar", null, null);
         assertActive(rm, true, "foo", "bar");
         assertActive(rm, false, "wiz", "bah", "");
     }
 
-    @org.junit.Test public void testOptions() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar", "a,b,c|d,e,f", null));
+    @org.junit.Test
+    public void testOptions() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar", "a,b,c|d,e,f", null);
         assertActive(rm, true, "foo", "bar", "a", "d");
         assertActive(rm, false, "b", "c", "e", "f");
     }
 
-    @org.junit.Test public void testEmptyRunModesWithOptions() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("", "a,b,c|d,e,f", null));
+    @org.junit.Test
+    public void testEmptyRunModesWithOptions() {
+        final SlingSettingsService rm = createSlingSettingsService("", "a,b,c|d,e,f", null);
         assertActive(rm, true, "a", "d");
         assertActive(rm, false, "b", "c", "e", "f");
     }
 
-    @org.junit.Test public void testOptionsSelected() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar,c,e", "a,b,c|d,e,f", null));
+    @org.junit.Test
+    public void testOptionsSelected() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar,c,e", "a,b,c|d,e,f", null);
         assertActive(rm, true, "foo", "bar", "c", "e");
         assertActive(rm, false, "a", "b", "d", "f");
     }
 
-    @org.junit.Test public void testOptionsMultipleSelected() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar,c,e,f,a", "a,b,c|d,e,f", null));
+    @org.junit.Test
+    public void testOptionsMultipleSelected() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar,c,e,f,a", "a,b,c|d,e,f", null);
         assertActive(rm, true, "foo", "bar", "a", "e");
         assertActive(rm, false, "b", "c", "d", "f");
     }
 
-    @org.junit.Test public void testOptionsMultipleSelected2() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar,c,f,a,d", "a,b,c|d,e,f", null));
+    @org.junit.Test
+    public void testOptionsMultipleSelected2() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar,c,f,a,d", "a,b,c|d,e,f", null);
         assertActive(rm, true, "foo", "bar", "a", "d");
         assertActive(rm, false, "b", "c", "e", "f");
     }
 
-    @org.junit.Test public void testInstallOptions() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar", null, "a,b,c|d,e,f"));
+    @org.junit.Test
+    public void testInstallOptions() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar", null, "a,b,c|d,e,f");
         assertActive(rm, true, "foo", "bar", "a", "d");
         assertActive(rm, false, "b", "c", "e", "f");
     }
 
-    @org.junit.Test public void testInstallOptionsSelected() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar,c,e", null , "a,b,c|d,e,f"));
+    @org.junit.Test
+    public void testInstallOptionsSelected() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar,c,e", null, "a,b,c|d,e,f");
         assertActive(rm, true, "foo", "bar", "c", "e");
         assertActive(rm, false, "a", "b", "d", "f");
     }
 
-    @org.junit.Test public void testInstallOptionsMultipleSelected() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar,c,e,f,a", null, "a,b,c|d,e,f"));
+    @org.junit.Test
+    public void testInstallOptionsMultipleSelected() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar,c,e,f,a", null, "a,b,c|d,e,f");
         assertActive(rm, true, "foo", "bar", "a", "e");
         assertActive(rm, false, "b", "c", "d", "f");
     }
 
-    @org.junit.Test public void testInstallOptionsMultipleSelected2() {
-        final SlingSettingsService rm = new SlingSettingsServiceImpl(new BundleContextMock("foo,bar,c,d,f,a", null, "a,b,c|d,e,f"));
+    @org.junit.Test
+    public void testInstallOptionsMultipleSelected2() {
+        final SlingSettingsService rm = createSlingSettingsService("foo,bar,c,d,f,a", null, "a,b,c|d,e,f");
         assertActive(rm, true, "foo", "bar", "a", "d");
         assertActive(rm, false, "b", "c", "e", "f");
     }
 
-    @org.junit.Test public void testInstallOptionsRestart() {
-        final BundleContextMock bc = new BundleContextMock("foo,bar,c,e,f,a", null, "a,b,c|d,e,f");
-
+    @org.junit.Test
+    public void testInstallOptionsRestart() {
         {
             // create first context to simulate install
-            final SlingSettingsService rm = new SlingSettingsServiceImpl(bc);
-            assertActive(rm, true, "foo", "bar", "a", "e");
-            assertActive(rm, false, "b", "c", "d", "f");
-        }
-
-        {
-            final SlingSettingsService rm = new SlingSettingsServiceImpl(bc);
+            final SlingSettingsService rm = createSlingSettingsService("foo,bar,c,e,f,a", null, "a,b,c|d,e,f");
             assertActive(rm, true, "foo", "bar", "a", "e");
             assertActive(rm, false, "b", "c", "d", "f");
         }
 
         // simulate restart with different run modes: new ones that are
         // mentioned in the .options properties are ignored
-        bc.update("foo,doo,a,b,c,d,e,f,waa");
         {
-            final SlingSettingsService rm = new SlingSettingsServiceImpl(bc);
+            SlingSettingsService rm = createSlingSettingsService("foo,bar,c,e,f,a", null, "a,b,c|d,e,f");
+            assertActive(rm, true, "foo", "bar", "a", "e");
+            assertActive(rm, false, "b", "c", "d", "f");
+            rm = createSlingSettingsService("foo,doo,a,b,c,d,e,f,waa", null, "a,b,c|d,e,f");
             assertActive(rm, true, "foo", "doo", "a", "e", "waa");
             assertActive(rm, false, "bar", "b", "c", "d", "f");
         }
     }
 
-    private static final class BundleContextMock implements BundleContext {
-
-        private String runModes;
-        private final String options;
-        private final String installOptions;
-
-        private final Map<String, File> files = new HashMap<String, File>();
-
-        public BundleContextMock(String runModes, String options, String installOptions) {
-            this.runModes = runModes;
-            this.options = options;
-            this.installOptions = installOptions;
-        }
-
-        public void update(final String rm) {
-            this.runModes = rm;
-        }
-
-        @Override
-        public void addBundleListener(BundleListener listener) {
-            // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        public void addFrameworkListener(FrameworkListener listener) {
-            // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        public void addServiceListener(ServiceListener listener, String filter)
-                throws InvalidSyntaxException {
-            // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        public void addServiceListener(ServiceListener listener) {
-            // TODO Auto-generated method stub
-
-        }
-
-        @Override
-        public Filter createFilter(String filter) throws InvalidSyntaxException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public ServiceReference[] getAllServiceReferences(String clazz,
-                String filter) throws InvalidSyntaxException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public Bundle getBundle() {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public Bundle getBundle(long id) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public Bundle[] getBundles() {
-            return new Bundle[0];
-        }
-
-        @Override
-        public File getDataFile(String filename) {
-            File f = files.get(filename);
-            if ( f == null ) {
-                try {
-                    f = File.createTempFile(filename, "id");
-                    f.delete();
-                    files.put(filename, f);
-                } catch (IOException ioe) {
-                    throw new RuntimeException(ioe);
-                }
-            }
-            return f;
-        }
-
-        @Override
-        public String getProperty(String key) {
-            if ( key.equals(SlingSettingsService.RUN_MODES_PROPERTY) ) {
-                return runModes;
-            } else if ( key.equals(SlingSettingsService.RUN_MODE_OPTIONS) ) {
-                return options;
-            } else if ( key.equals(SlingSettingsService.RUN_MODE_INSTALL_OPTIONS) ) {
-                return installOptions;
-            }
-            return null;
-        }
-
-        @Override
-        public Object getService(ServiceReference reference) {
-            return null;
-        }
-
-        @Override
-        public ServiceReference getServiceReference(String clazz) {
-            return null;
-        }
-
-        @Override
-        public ServiceReference[] getServiceReferences(String clazz,
-                String filter) throws InvalidSyntaxException {
-            return null;
-        }
-
-        @Override
-        public Bundle installBundle(String location, InputStream input)
-                throws BundleException {
-            return null;
-        }
-
-        @Override
-        public Bundle installBundle(String location) throws BundleException {
-            return null;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public ServiceRegistration registerService(String clazz,
-                Object service, Dictionary properties) {
-            return null;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public ServiceRegistration registerService(String[] clazzes,
-                Object service, Dictionary properties) {
-            return null;
-        }
-
-        @Override
-        public void removeBundleListener(BundleListener listener) {
-        }
-
-        @Override
-        public void removeFrameworkListener(FrameworkListener listener) {
-        }
-
-        @Override
-        public void removeServiceListener(ServiceListener listener) {
-        }
-
-        @Override
-        public boolean ungetService(ServiceReference reference) {
-            return false;
-        }
-
-        @Override
-        public <S> ServiceRegistration<S> registerService(Class<S> clazz, S service, Dictionary<String, ?> properties) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public <S> ServiceRegistration<S> registerService(Class<S> clazz, ServiceFactory<S> factory,
-                Dictionary<String, ?> properties) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public <S> ServiceReference<S> getServiceReference(Class<S> clazz) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> clazz, String filter)
-                throws InvalidSyntaxException {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public <S> ServiceObjects<S> getServiceObjects(ServiceReference<S> reference) {
-            // TODO Auto-generated method stub
-            return null;
-        }
-
-        @Override
-        public Bundle getBundle(String location) {
-            // TODO Auto-generated method stub
-            return null;
-        }
+    private SlingSettingsService createSlingSettingsService(String runModes, String options, String installOptions) {
+        this.runModes = runModes;
+        this.options = options;
+        this.installOptions = installOptions;
+        return new SlingSettingsServiceImpl(configuration, mockBundleContext);
     }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/settings/impl/SlingSettingsServiceImplTest.java b/src/test/java/org/apache/sling/settings/impl/SlingSettingsServiceImplTest.java
index 15c935f..e47bc46 100644
--- a/src/test/java/org/apache/sling/settings/impl/SlingSettingsServiceImplTest.java
+++ b/src/test/java/org/apache/sling/settings/impl/SlingSettingsServiceImplTest.java
@@ -29,6 +29,7 @@ import java.io.IOException;
 import java.io.ObjectOutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -40,6 +41,8 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.BundleContext;
+import org.osgi.util.converter.Converter;
+import org.osgi.util.converter.Converters;
 
 public class SlingSettingsServiceImplTest {
 
@@ -50,11 +53,16 @@ public class SlingSettingsServiceImplTest {
     private File slingIdFile = null;
 
     private File optionsFile = null;
+    
+    private SlingSettingsServiceImpl.Configuration configuration;
 
     @Before
     public void before() throws IOException {
         slingIdFile = File.createTempFile(SLING_ID_FILE_NAME, "");
         optionsFile = File.createTempFile(OPTIONS_FILE_NAME, "");
+        Converter c = Converters.standardConverter();
+        // use standard configuration
+        configuration = c.convert(new HashMap<String, Object>()).to(SlingSettingsServiceImpl.Configuration.class);
     }
 
     @After
@@ -132,7 +140,7 @@ public class SlingSettingsServiceImplTest {
         } catch (final IOException ioe) {
             throw new RuntimeException("Unable to write to options data file.", ioe);
         }
-        return new SlingSettingsServiceImpl(context);
+        return new SlingSettingsServiceImpl(configuration, context);
     }
 
 }