You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2017/04/24 07:48:46 UTC

svn commit: r1792431 - in /felix/trunk/osgi-r7/configurator/src: main/java/org/apache/felix/configurator/impl/ main/java/org/apache/felix/configurator/impl/model/ test/java/org/apache/felix/configurator/impl/

Author: cziegeler
Date: Mon Apr 24 07:48:46 2017
New Revision: 1792431

URL: http://svn.apache.org/viewvc?rev=1792431&view=rev
Log:
Start implementing correct configuration admin handling (WiP)

Modified:
    felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Configurator.java
    felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/ServicesListener.java
    felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Util.java
    felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java
    felix/trunk/osgi-r7/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java

Modified: felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Configurator.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Configurator.java?rev=1792431&r1=1792430&r2=1792431&view=diff
==============================================================================
--- felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Configurator.java (original)
+++ felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Configurator.java Mon Apr 24 07:48:46 2017
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -30,6 +32,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 
+import org.apache.felix.configurator.impl.json.JSONUtil;
 import org.apache.felix.configurator.impl.logger.SystemLogger;
 import org.apache.felix.configurator.impl.model.BundleState;
 import org.apache.felix.configurator.impl.model.Config;
@@ -41,7 +44,9 @@ import org.apache.felix.configurator.imp
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.osgi.service.configurator.ConfiguratorConstants;
@@ -55,8 +60,6 @@ public class Configurator {
 
     private final BundleContext bundleContext;
 
-    private final ConfigurationAdmin configAdmin;
-
     private final State state;
 
     private final Set<String> activeEnvironments;
@@ -69,15 +72,18 @@ public class Configurator {
 
     private final WorkerQueue queue;
 
+    private final List<ServiceReference<ConfigurationAdmin>> configAdminReferences;
+
     /**
      * Create a new configurator and start it
+     *
      * @param bc The bundle context
-     * @param ca The configuration admin
+     * @param configAdminReferences Dynamic list of references to the configuration admin service visible to the configurator
      */
-    public Configurator(final BundleContext bc, final ConfigurationAdmin ca) {
+    public Configurator(final BundleContext bc, final List<ServiceReference<ConfigurationAdmin>> configAdminReferences) {
         this.queue = new WorkerQueue();
         this.bundleContext = bc;
-        this.configAdmin = ca;
+        this.configAdminReferences = configAdminReferences;
         this.activeEnvironments = Util.getActiveEnvironments(bc);
         this.state = State.createOrReadState(bundleContext);
         this.state.changeEnvironments(this.activeEnvironments);
@@ -121,6 +127,7 @@ public class Configurator {
                             try {
                                 processRemoveBundle(bundle.getBundleId());
                                 process();
+                                Configurator.this.state.removeConfigAdminBundleId(bundle.getBundleId());
                             } catch ( final IllegalStateException ise) {
                                 SystemLogger.error("Error processing bundle " + getBundleIdentity(bundle), ise);
                             }
@@ -280,7 +287,7 @@ public class Configurator {
             }
             final Set<String> paths = Util.isConfigurerBundle(bundle, this.bundleContext.getBundle().getBundleId());
             if ( paths != null ) {
-                final BundleState config = org.apache.felix.configurator.impl.json.JSONUtil.readConfigurationsFromBundle(bundle, paths);
+                final BundleState config = JSONUtil.readConfigurationsFromBundle(bundle, paths);
                 for(final String pid : config.getPids()) {
                     state.addAll(pid, config.getConfigurations(pid));
                 }
@@ -380,7 +387,7 @@ public class Configurator {
 
         }
         // if there is a configuration to activate, we can directly activate it
-        // without deactivating (reduced the changes of the configuration from two
+        // without deactivating (reducing the changes of the configuration from two
         // to one)
         if ( toActivate != null && toActivate.getState() == ConfigState.INSTALL ) {
             activate(configList, toActivate);
@@ -414,27 +421,80 @@ public class Configurator {
         configList.setHasChanges(false);
     }
 
+    private ConfigurationAdmin getConfigurationAdmin(final long configAdminServiceBundleId) {
+        ServiceReference<ConfigurationAdmin> ref = null;
+        synchronized ( this.configAdminReferences ) {
+            for(final ServiceReference<ConfigurationAdmin> r : this.configAdminReferences ) {
+                final Bundle bundle = r.getBundle();
+                if ( bundle != null && bundle.getBundleId() == configAdminServiceBundleId) {
+                    ref = r;
+                    break;
+                }
+            }
+        }
+        if ( ref != null ) {
+            return this.bundleContext.getService(ref);
+        }
+        return null;
+    }
+
     /**
      * Try to activate a configuration
      * Check policy and change count
-     * @param cfg The configuration
+     * @param configList The configuration list
+     * @param cfg The configuration to activate
+     * @return {@code true} if activation was successful
      */
-    public void activate(final ConfigList configList, final Config cfg) {
+    public boolean activate(final ConfigList configList, final Config cfg) {
+        // check for configuration admin
+        Long configAdminServiceBundleId = this.state.getConfigAdminBundleId(cfg.getBundleId());
+        if ( configAdminServiceBundleId == null ) {
+            final Bundle configBundle = this.bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext().getBundle(cfg.getBundleId());
+            if ( configBundle != null ) {
+                try {
+                    final Collection<ServiceReference<ConfigurationAdmin>> refs = configBundle.getBundleContext().getServiceReferences(ConfigurationAdmin.class, null);
+                    final List<ServiceReference<ConfigurationAdmin>> sortedRefs = new ArrayList<>(refs);
+                    Collections.sort(sortedRefs);
+                    for(int i=sortedRefs.size();i>0;i--) {
+                        final ServiceReference<ConfigurationAdmin> r = sortedRefs.get(i-1);
+                        synchronized ( this.configAdminReferences ) {
+                            if ( this.configAdminReferences.contains(r) ) {
+                                configAdminServiceBundleId = r.getBundle().getBundleId();
+                                break;
+                            }
+                        }
+                    }
+                } catch (final InvalidSyntaxException e) {
+                    // this can never happen as we pass {@code null} as the filter
+                }
+            }
+        }
+        if ( configAdminServiceBundleId == null ) {
+            // no configuration admin found, we have to retry
+            return false;
+        }
+        final ConfigurationAdmin configAdmin = this.getConfigurationAdmin(configAdminServiceBundleId);
+        if ( configAdmin == null ) {
+            // getting configuration admin failed, we have to retry
+            return false;
+        }
+        this.state.setConfigAdminBundleId(cfg.getBundleId(), configAdminServiceBundleId);
+
         boolean ignore = false;
         try {
             // get existing configuration - if any
             boolean update = false;
-            Configuration configuration = ConfigUtil.getOrCreateConfiguration(this.configAdmin, cfg.getPid(), false);
+            Configuration configuration = ConfigUtil.getOrCreateConfiguration(configAdmin, cfg.getPid(), false);
             if ( configuration == null ) {
                 // new configuration
-                configuration = ConfigUtil.getOrCreateConfiguration(this.configAdmin, cfg.getPid(), true);
+                configuration = ConfigUtil.getOrCreateConfiguration(configAdmin, cfg.getPid(), true);
                 update = true;
             } else {
                 if ( cfg.getPolicy() == ConfigPolicy.FORCE ) {
                     update = true;
                 } else {
                     if ( configList.getLastInstalled() == null
-                            || configList.getChangeCount() != configuration.getChangeCount() ) {
+                         || configList.getChangeCount() != configuration.getChangeCount() ) {
                         ignore = true;
                     } else {
                         update = true;
@@ -457,6 +517,8 @@ public class Configurator {
             configList.setChangeCount(-1);
             configList.setLastInstalled(null);
         }
+
+        return true;
     }
 
     /**
@@ -464,20 +526,34 @@ public class Configurator {
      * Check policy and change count
      * @param cfg The configuration
      */
-    public void deactivate(final ConfigList configList, final Config cfg) {
-        try {
-            final Configuration c = ConfigUtil.getOrCreateConfiguration(this.configAdmin, cfg.getPid(), false);
-            if ( c != null ) {
-                if ( cfg.getPolicy() == ConfigPolicy.FORCE
-                        || configList.getChangeCount() == c.getChangeCount() ) {
-                    c.delete();
+    public boolean deactivate(final ConfigList configList, final Config cfg) {
+        final Long configAdminServiceBundleId = this.state.getConfigAdminBundleId(cfg.getBundleId());
+        // check if configuration admin bundle is still available
+        // if not or if we didn't record anything, we consider the configuration uninstalled
+        final Bundle configBundle = configAdminServiceBundleId == null ? null : this.bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext().getBundle(configAdminServiceBundleId);
+        if ( configBundle != null ) {
+            final ConfigurationAdmin configAdmin = this.getConfigurationAdmin(configAdminServiceBundleId);
+            if ( configAdmin == null ) {
+                // getting configuration admin failed, we have to retry
+                return false;
+            }
+
+            try {
+                final Configuration c = ConfigUtil.getOrCreateConfiguration(configAdmin, cfg.getPid(), false);
+                if ( c != null ) {
+                    if ( cfg.getPolicy() == ConfigPolicy.FORCE
+                            || configList.getChangeCount() == c.getChangeCount() ) {
+                        c.delete();
+                    }
                 }
+            } catch (final InvalidSyntaxException | IOException e) {
+                SystemLogger.error("Unable to remove configuration " + cfg.getPid() + " : " + e.getMessage(), e);
             }
-        } catch (final InvalidSyntaxException | IOException e) {
-            SystemLogger.error("Unable to remove configuration " + cfg.getPid() + " : " + e.getMessage(), e);
         }
         cfg.setState(ConfigState.UNINSTALLED);
         configList.setChangeCount(-1);
         configList.setLastInstalled(null);
+
+        return true;
     }
 }

Modified: felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/ServicesListener.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/ServicesListener.java?rev=1792431&r1=1792430&r2=1792431&view=diff
==============================================================================
--- felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/ServicesListener.java (original)
+++ felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/ServicesListener.java Mon Apr 24 07:48:46 2017
@@ -18,6 +18,10 @@
  */
 package org.apache.felix.configurator.impl;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.felix.configurator.impl.logger.SystemLogger;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
@@ -26,6 +30,8 @@ import org.osgi.framework.ServiceEvent;
 import org.osgi.framework.ServiceListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
 /**
  * The {@code ServicesListener} listens for the required services
@@ -37,8 +43,8 @@ public class ServicesListener {
     /** The bundle context. */
     private final BundleContext bundleContext;
 
-    /** The listener for the config admin. */
-    private final Listener caListener;
+    /** The service tracker for configuration admin */
+    private final ServiceTracker<ConfigurationAdmin, ServiceReference<ConfigurationAdmin>> caTracker;
 
     /** The listener for the coordinator. */
     private final Listener coordinatorListener;
@@ -46,14 +52,45 @@ public class ServicesListener {
     /** The current configurator. */
     private volatile Configurator configurator;
 
+    private final List<ServiceReference<ConfigurationAdmin>> configAdminReferences;
+
     /**
      * Start listeners
      */
     public ServicesListener(final BundleContext bundleContext) {
         this.bundleContext = bundleContext;
-        this.caListener = new Listener(ConfigurationAdmin.class.getName());
+        this.configAdminReferences = new ArrayList<>();
+        this.caTracker = new ServiceTracker<>(bundleContext, ConfigurationAdmin.class,
+
+                new ServiceTrackerCustomizer<ConfigurationAdmin, ServiceReference<ConfigurationAdmin>>() {
+
+            @Override
+            public ServiceReference<ConfigurationAdmin> addingService(final ServiceReference<ConfigurationAdmin> reference) {
+                synchronized ( configAdminReferences ) {
+                    configAdminReferences.add(reference);
+                    Collections.sort(configAdminReferences);
+                }
+                notifyChange();
+                return reference;
+            }
+
+            @Override
+            public void modifiedService(final ServiceReference<ConfigurationAdmin> reference,
+                    final ServiceReference<ConfigurationAdmin> service) {
+                // nothing to do
+            }
+
+            @Override
+            public void removedService(final ServiceReference<ConfigurationAdmin> reference,
+                    final ServiceReference<ConfigurationAdmin> service) {
+                synchronized ( configAdminReferences ) {
+                    configAdminReferences.remove(reference);
+                }
+                notifyChange();
+            }
+        });
         this.coordinatorListener = new Listener("org.osgi.service.coordinator.Coordinator");
-        this.caListener.start();
+        this.caTracker.open();
         this.coordinatorListener.start();
         SystemLogger.debug("Started services listener for configurator.");
     }
@@ -62,27 +99,29 @@ public class ServicesListener {
      * Notify of service changes from the listeners.
      * If all services are available, start
      */
-    public synchronized void notifyChange() {
-        // check if all services are available
-        final ConfigurationAdmin ca = (ConfigurationAdmin)this.caListener.getService();
-        final Object coordinator = this.coordinatorListener.getService();
-        SystemLogger.debug("Services updated for configurator: " + ca + " - " + coordinator);
-
-        if ( ca != null ) {
-            boolean isNew = configurator == null;
-            if ( isNew ) {
-                SystemLogger.debug("Starting new configurator");
-                configurator = new Configurator(this.bundleContext, ca);
-            }
-            configurator.setCoordinator(coordinator);
-            if ( isNew ) {
-                configurator.start();
-            }
-        } else {
-            if ( configurator != null ) {
-                SystemLogger.debug("Stopping configurator");
-                configurator.shutdown();
-                configurator = null;
+    public void notifyChange() {
+        synchronized ( configAdminReferences ) {
+            // check if there is at least a single configuration admin
+            final boolean hasConfigAdmin = !this.configAdminReferences.isEmpty();
+            final Object coordinator = this.coordinatorListener.getService();
+            SystemLogger.debug("Services updated for configurator: " + configAdminReferences + " - " + coordinator);
+
+            if ( hasConfigAdmin ) {
+                boolean isNew = configurator == null;
+                if ( isNew ) {
+                    SystemLogger.debug("Starting new configurator");
+                    configurator = new Configurator(this.bundleContext, this.configAdminReferences);
+                }
+                configurator.setCoordinator(coordinator);
+                if ( isNew ) {
+                    configurator.start();
+                }
+            } else {
+                if ( configurator != null ) {
+                    SystemLogger.debug("Stopping configurator");
+                    configurator.shutdown();
+                    configurator = null;
+                }
             }
         }
     }
@@ -91,7 +130,7 @@ public class ServicesListener {
      * Deactivate this listener.
      */
     public void deactivate() {
-        this.caListener.deactivate();
+        this.caTracker.close();
         this.coordinatorListener.deactivate();
         if ( configurator != null ) {
             configurator.shutdown();

Modified: felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Util.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Util.java?rev=1792431&r1=1792430&r2=1792431&view=diff
==============================================================================
--- felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Util.java (original)
+++ felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/Util.java Mon Apr 24 07:48:46 2017
@@ -59,6 +59,7 @@ public class Util {
     /**
      * Check if the bundle contains configurations for the configurator
      * @param bundle The bundle
+     * @param configuratorBundleId The bundle id of the configurator bundle to check the wiring
      * @return Set of locations or {@code null}
      */
     @SuppressWarnings("unchecked")

Modified: felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java?rev=1792431&r1=1792430&r2=1792431&view=diff
==============================================================================
--- felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java (original)
+++ felix/trunk/osgi-r7/configurator/src/main/java/org/apache/felix/configurator/impl/model/State.java Mon Apr 24 07:48:46 2017
@@ -43,13 +43,15 @@ public class State extends AbstractState
 
     private static final String FILE_NAME = "state.ser";
 
-    private final Map<Long, Long> bundlesLastModified = new HashMap<Long, Long>();
+    private final Map<Long, Long> bundlesLastModified = new HashMap<>();
+
+    private final Map<Long, Long> bundlesConfigAdminBundleId = new HashMap<>();
 
     private final Set<String> environments = new HashSet<>();
 
     private volatile Set<String> initialHashes;
 
-    volatile transient boolean envsChanged = true;
+    private volatile transient boolean envsChanged = true;
 
     /**
      * Serialize the object
@@ -122,6 +124,18 @@ public class State extends AbstractState
         this.bundlesLastModified.remove(bundleId);
     }
 
+    public Long getConfigAdminBundleId(final long bundleId) {
+        return this.bundlesConfigAdminBundleId.get(bundleId);
+    }
+
+    public void setConfigAdminBundleId(final long bundleId, final long lastModified) {
+        this.bundlesConfigAdminBundleId.put(bundleId, lastModified);
+    }
+
+    public void removeConfigAdminBundleId(final long bundleId) {
+        this.bundlesConfigAdminBundleId.remove(bundleId);
+    }
+
     public Set<Long> getKnownBundleIds() {
         return this.bundlesLastModified.keySet();
     }

Modified: felix/trunk/osgi-r7/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java?rev=1792431&r1=1792430&r2=1792431&view=diff
==============================================================================
--- felix/trunk/osgi-r7/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java (original)
+++ felix/trunk/osgi-r7/configurator/src/test/java/org/apache/felix/configurator/impl/ConfiguratorTest.java Mon Apr 24 07:48:46 2017
@@ -38,6 +38,7 @@ import org.mockito.InOrder;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleWire;
 import org.osgi.framework.wiring.BundleWiring;
@@ -54,19 +55,29 @@ public class ConfiguratorTest {
 
     private ConfigurationAdmin configurationAdmin;
 
+    private ServiceReference<ConfigurationAdmin> caRef;
+
+    @SuppressWarnings("unchecked")
     @Before public void setup() throws IOException {
         bundle = mock(Bundle.class);
         when(bundle.getBundleId()).thenReturn(42L);
         bundleContext = mock(BundleContext.class);
+        when(bundle.getBundleContext()).thenReturn(bundleContext);
         when(bundleContext.getBundle()).thenReturn(bundle);
+        when(bundleContext.getBundle(Constants.SYSTEM_BUNDLE_LOCATION)).thenReturn(bundle);
+        when(bundleContext.getBundle(42)).thenReturn(bundle);
         when(bundleContext.getBundles()).thenReturn(new Bundle[0]);
         when(bundleContext.getDataFile("binaries" + File.separatorChar + ".check")).thenReturn(Files.createTempDirectory("test").toFile());
+        caRef = mock(ServiceReference.class);
+        when(caRef.getBundle()).thenReturn(bundle);
+
         configurationAdmin = mock(ConfigurationAdmin.class);
+        when(bundleContext.getService(caRef)).thenReturn(configurationAdmin);
 
-        configurator = new Configurator(bundleContext, configurationAdmin);
+        configurator = new Configurator(bundleContext, Collections.singletonList(caRef));
     }
 
-    private Bundle setupBundle(final long id) {
+    private Bundle setupBundle(final long id) throws Exception {
         final Bundle b = mock(Bundle.class);
         when(b.getBundleId()).thenReturn(id);
         when(b.getLastModified()).thenReturn(5L);
@@ -83,6 +94,10 @@ public class ConfiguratorTest {
         urls.add(this.getClass().getResource("/bundles/" + id + ".json"));
         when(b.findEntries("OSGI-INF/configurator", "*.json", false)).thenReturn(urls.elements());
 
+        final BundleContext bContext = mock(BundleContext.class);
+        when(b.getBundleContext()).thenReturn(bContext);
+        when(bContext.getServiceReferences(ConfigurationAdmin.class, null)).thenReturn(Collections.singleton(caRef));
+        when(bundleContext.getBundle(id)).thenReturn(b);
         return b;
     }