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;
}