You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ch...@apache.org on 2014/05/27 08:14:30 UTC
svn commit: r1597707 - in /jackrabbit/oak/trunk/oak-pojosr: ./
src/main/java/org/apache/jackrabbit/oak/run/osgi/ src/test/groovy/
src/test/groovy/org/ src/test/groovy/org/apache/
src/test/groovy/org/apache/jackrabbit/ src/test/groovy/org/apache/jackrab...
Author: chetanm
Date: Tue May 27 06:14:29 2014
New Revision: 1597707
URL: http://svn.apache.org/r1597707
Log:
OAK-1856 - Enable specifying of OSGi config via JSON file and in memory map
Added required support and also added Groovy based testcase
Added:
jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/ConfigTracker.java (with props)
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/
jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/ConfigTest.groovy
Modified:
jackrabbit/oak/trunk/oak-pojosr/pom.xml
jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/OakOSGiRepositoryFactory.java
Modified: jackrabbit/oak/trunk/oak-pojosr/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-pojosr/pom.xml?rev=1597707&r1=1597706&r2=1597707&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-pojosr/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-pojosr/pom.xml Tue May 27 06:14:29 2014
@@ -32,9 +32,37 @@
<properties>
<skip.deployment>true</skip.deployment>
+ <groovy.version>2.3.1</groovy.version>
</properties>
<build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.gmaven</groupId>
+ <artifactId>gmaven-plugin</artifactId>
+ <version>1.4</version>
+ <!--suppress MavenModelInspection -->
+ <configuration>
+ <providerSelection>2.0</providerSelection>
+ <sourceEncoding>UTF-8</sourceEncoding>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generateTestStubs</goal>
+ <goal>testCompile</goal>
+ </goals>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <version>${groovy.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
<pluginManagement>
<plugins>
<plugin>
@@ -107,18 +135,16 @@
</dependency>
<!-- Pojo SR -->
-
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.3.1</version>
+ </dependency>
<dependency>
<groupId>com.googlecode.pojosr</groupId>
<artifactId>de.kalpatec.pojosr.framework.bare</artifactId>
<version>0.2.1</version>
</dependency>
-
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- <version>4.3.0</version>
- </dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr</artifactId>
@@ -139,6 +165,11 @@
<artifactId>org.apache.felix.jaas</artifactId>
<version>0.0.2</version>
</dependency>
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ <version>1.1.1</version>
+ </dependency>
<!-- Test dependencies -->
<dependency>
@@ -146,5 +177,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <version>${groovy.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
Added: jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/ConfigTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/ConfigTracker.java?rev=1597707&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/ConfigTracker.java (added)
+++ jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/ConfigTracker.java Tue May 27 06:14:29 2014
@@ -0,0 +1,260 @@
+/*
+ * 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.jackrabbit.oak.run.osgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import org.apache.felix.utils.collections.DictionaryAsMap;
+import org.apache.felix.utils.properties.InterpolationHelper;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import org.osgi.framework.BundleContext;
+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.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Installs config obtained from JSON Config file or passed as part of
+ * startup
+ */
+class ConfigTracker extends ServiceTracker {
+ private static final String MARKER_NAME = "oak.configinstall.name";
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private ConfigurationAdmin cm;
+ private final Map config;
+ private final BundleContext bundleContext;
+
+ public ConfigTracker(Map config, BundleContext bundleContext) {
+ super(bundleContext, ConfigurationAdmin.class.getName(), null);
+ this.config = config;
+ this.bundleContext = bundleContext;
+ open();
+ }
+
+ @Override
+ public Object addingService(ServiceReference reference) {
+ cm = (ConfigurationAdmin) super.addingService(reference);
+ try {
+ synchronizeConfigs();
+ } catch (Exception e) {
+ log.error("Error occurred while installing configs", e);
+ throw new RuntimeException(e);
+ }
+ return cm;
+ }
+
+ /**
+ * Synchronizes the configs. All config added by this class is also kept in sync with re runs
+ * i.e. if a config was added in first run and say later removed then that would also be removed
+ * from the ConfigurationAdmin
+ */
+ private void synchronizeConfigs() throws Exception {
+ Set<String> existingPids = determineExistingConfigs();
+ Set<String> processedPids = Sets.newHashSet();
+
+ Map<String, Map<String, Object>> configs = Maps.newHashMap();
+
+ Map<String, Map<String, Object>> configFromFile =
+ parseJSONConfig((String) config.get(OakOSGiRepositoryFactory.REPOSITORY_CONFIG_FILE));
+ configs.putAll(configFromFile);
+
+ @SuppressWarnings("unchecked")
+ Map<String, Map<String, Object>> runtimeConfig =
+ (Map<String, Map<String, Object>>) config.get(OakOSGiRepositoryFactory.REPOSITORY_CONFIG);
+ if (runtimeConfig != null) {
+ configs.putAll(runtimeConfig);
+ }
+
+ installConfigs(configs, processedPids);
+ Set<String> pidsToBeRemoved = Sets.difference(existingPids, processedPids);
+ removeConfigs(pidsToBeRemoved);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<String, Map<String, Object>> parseJSONConfig(String jsonFilePath) throws IOException {
+ Map<String, Map<String, Object>> configs = Maps.newHashMap();
+ if (jsonFilePath == null) {
+ return configs;
+ }
+
+ List<String> files = Splitter.on(',').trimResults().omitEmptyStrings().splitToList(jsonFilePath);
+ for (String filePath : files) {
+ File jsonFile = new File(filePath);
+ if (!jsonFile.exists()) {
+ log.warn("No file found at path {}. Ignoring the file entry", jsonFile.getAbsolutePath());
+ continue;
+ }
+
+ String content = Files.toString(jsonFile, Charsets.UTF_8);
+ JSONObject json = (JSONObject) JSONValue.parse(content);
+ configs.putAll(json);
+ }
+
+ return configs;
+ }
+
+ private void removeConfigs(Set<String> pidsToBeRemoved) throws Exception {
+ for (String pidString : pidsToBeRemoved) {
+ String[] pid = parsePid(pidString);
+ Configuration config = getConfiguration(pidString, pid[0], pid[1]);
+ config.delete();
+ }
+
+ if (!pidsToBeRemoved.isEmpty()) {
+ log.info("Configuration belonging to following pids have been removed ", pidsToBeRemoved);
+ }
+ }
+
+ private void installConfigs(Map<String, Map<String, Object>> osgiConfig, Set<String> processedPids)
+ throws Exception {
+ for (Map.Entry<String, Map<String, Object>> pidEntry : osgiConfig.entrySet()) {
+ final String pidString = pidEntry.getKey();
+
+ final Hashtable<String, Object> current = new Hashtable<String, Object>();
+ current.putAll(pidEntry.getValue());
+ performSubstitution(current);
+
+ String[] pid = parsePid(pidString);
+ Configuration config = getConfiguration(pidString, pid[0], pid[1]);
+
+ @SuppressWarnings("unchecked") Dictionary<String, Object> props = config.getProperties();
+ Hashtable<String, Object> old = props != null ?
+ new Hashtable<String, Object>(new DictionaryAsMap<String, Object>(props)) : null;
+ if (old != null) {
+ old.remove(MARKER_NAME);
+ old.remove(Constants.SERVICE_PID);
+ old.remove(ConfigurationAdmin.SERVICE_FACTORYPID);
+ }
+
+ if (!current.equals(old)) {
+ current.put(MARKER_NAME, pidString);
+ if (config.getBundleLocation() != null) {
+ config.setBundleLocation(null);
+ }
+ if (old == null) {
+ log.info("Creating configuration from {}", pidString);
+ } else {
+ log.info("Updating configuration from {}", pidString);
+ }
+ config.update(current);
+ processedPids.add(pidString);
+ }
+ }
+ }
+
+ private void performSubstitution(Hashtable<String, Object> current) {
+ Map<String, String> simpleConfig = Maps.newHashMap();
+
+ for (Map.Entry<String, Object> e : current.entrySet()) {
+ if (e.getValue() instanceof String) {
+ simpleConfig.put(e.getKey(), (String) e.getValue());
+ }
+ }
+
+ InterpolationHelper.performSubstitution(simpleConfig, bundleContext);
+
+ for (Map.Entry<String, String> e : simpleConfig.entrySet()) {
+ current.put(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * Determines the existing configs which are installed by ConfigInstaller
+ *
+ * @return set of pid strings
+ */
+ private Set<String> determineExistingConfigs() throws IOException, InvalidSyntaxException {
+ Set<String> pids = Sets.newHashSet();
+ String filter = "(" + MARKER_NAME + "=" + "*" + ")";
+ Configuration[] configurations = cm.listConfigurations(filter);
+ if (configurations != null) {
+ for (Configuration cfg : configurations) {
+ pids.add((String) cfg.getProperties().get(MARKER_NAME));
+ }
+ }
+ return pids;
+ }
+
+ private Configuration getConfiguration(String pidString, String pid, String factoryPid)
+ throws Exception {
+ Configuration oldConfiguration = findExistingConfiguration(pidString);
+ if (oldConfiguration != null) {
+ return oldConfiguration;
+ } else {
+ Configuration newConfiguration;
+ if (factoryPid != null) {
+ newConfiguration = cm.createFactoryConfiguration(pid, null);
+ } else {
+ newConfiguration = cm.getConfiguration(pid, null);
+ }
+ return newConfiguration;
+ }
+ }
+
+ private Configuration findExistingConfiguration(String pidString) throws Exception {
+ String filter = "(" + MARKER_NAME + "=" + escapeFilterValue(pidString) + ")";
+ Configuration[] configurations = cm.listConfigurations(filter);
+ if (configurations != null && configurations.length > 0) {
+ return configurations[0];
+ } else {
+ return null;
+ }
+ }
+
+ private static String escapeFilterValue(String s) {
+ return s.replaceAll("[(]", "\\\\(").
+ replaceAll("[)]", "\\\\)").
+ replaceAll("[=]", "\\\\=").
+ replaceAll("[\\*]", "\\\\*");
+ }
+
+ private static String[] parsePid(String pid) {
+ int n = pid.indexOf('-');
+ if (n > 0) {
+ String factoryPid = pid.substring(n + 1);
+ pid = pid.substring(0, n);
+ return new String[]
+ {
+ pid, factoryPid
+ };
+ } else {
+ return new String[]
+ {
+ pid, null
+ };
+ }
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/ConfigTracker.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/OakOSGiRepositoryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/OakOSGiRepositoryFactory.java?rev=1597707&r1=1597706&r2=1597707&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/OakOSGiRepositoryFactory.java (original)
+++ jackrabbit/oak/trunk/oak-pojosr/src/main/java/org/apache/jackrabbit/oak/run/osgi/OakOSGiRepositoryFactory.java Tue May 27 06:14:29 2014
@@ -37,6 +37,7 @@ import javax.jcr.RepositoryException;
import javax.jcr.RepositoryFactory;
import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
import com.google.common.util.concurrent.SettableFuture;
import de.kalpatec.pojosr.framework.launch.BundleDescriptor;
import de.kalpatec.pojosr.framework.launch.ClasspathScanner;
@@ -56,6 +57,7 @@ import static com.google.common.base.Pre
public class OakOSGiRepositoryFactory implements RepositoryFactory {
private static Logger log = LoggerFactory.getLogger(OakOSGiRepositoryFactory.class);
+
/**
* Name of the repository home parameter.
*/
@@ -63,7 +65,19 @@ public class OakOSGiRepositoryFactory im
= "org.apache.jackrabbit.repository.home";
public static final String REPOSITORY_STARTUP_TIMEOUT
- = "org.apache.jackrabbit.repository.startupTimeOut";
+ = "org.apache.jackrabbit.oak.repository.startupTimeOut";
+
+ /**
+ * Config key which refers to the map of config where key in that map refers to OSGi
+ * config
+ */
+ public static final String REPOSITORY_CONFIG = "org.apache.jackrabbit.oak.repository.config";
+
+ /**
+ * Comma separated list of file names which referred to config stored in form of JSON. The
+ * JSON content consist of pid as the key and config map as the value
+ */
+ public static final String REPOSITORY_CONFIG_FILE = "org.apache.jackrabbit.oak.repository.configFile";
/**
* Default timeout for repository creation
@@ -75,26 +89,11 @@ public class OakOSGiRepositoryFactory im
Map config = new HashMap();
config.putAll(parameters);
- //TODO Add support for passing config as map of PID -> Dictionary
- //as part of parameters and hook it up with Felix ConfigAdmin
- //Say via custom InMemory PersistenceManager or programatically
- //registering it with using ConfigAdmin API
- //For later part we would need to implement some sort of Start Level
- //support such that
- // 1. Some base bundles like ConfigAdmin get start first
- // 2. We register the user provided config
- // 3. Other bundles get started
-
//TODO With OSGi Whiteboard we need to provide support for handling
//execution and JMX support as so far they were provided by Sling bundles
//in OSGi env
- processConfig(config);
-
- PojoServiceRegistry registry = createServiceRegistry(config);
- preProcessRegistry(registry);
- startBundles(registry);
- postProcessRegistry(registry);
+ PojoServiceRegistry registry = initializeServiceRegistry(config);
//Future which would be used to notify when repository is ready
// to be used
@@ -126,6 +125,19 @@ public class OakOSGiRepositoryFactory im
}
}
+ @SuppressWarnings("unchecked")
+ PojoServiceRegistry initializeServiceRegistry(Map config) {
+ processConfig(config);
+
+ PojoServiceRegistry registry = createServiceRegistry(config);
+ startConfigTracker(registry, config);
+ preProcessRegistry(registry);
+ startBundles(registry);
+ postProcessRegistry(registry);
+
+ return registry;
+ }
+
/**
* Enables pre processing of service registry by sub classes. This can be
* used to register services before any bundle gets started
@@ -146,13 +158,7 @@ public class OakOSGiRepositoryFactory im
}
- /**
- * @param descriptors
- * @return the bundle descriptors
- */
protected List<BundleDescriptor> processDescriptors(List<BundleDescriptor> descriptors) {
- //If required sort the bundle descriptors such that configuration admin and file install bundle
- //gets started before SCR
Collections.sort(descriptors, new BundleDescriptorComparator());
return descriptors;
}
@@ -163,6 +169,10 @@ public class OakOSGiRepositoryFactory im
}
}
+ private static void startConfigTracker(PojoServiceRegistry registry, Map config) {
+ new ConfigTracker(config, registry.getBundleContext());
+ }
+
private static int getTimeoutInSeconds(Map config) {
Integer timeout = (Integer) config.get(REPOSITORY_STARTUP_TIMEOUT);
if (timeout == null) {
@@ -171,6 +181,7 @@ public class OakOSGiRepositoryFactory im
return timeout;
}
+ @SuppressWarnings("unchecked")
private static void processConfig(Map config) {
String home = (String) config.get(REPOSITORY_HOME);
checkNotNull(home, "Repository home not defined via [%s]", REPOSITORY_HOME);
@@ -193,12 +204,12 @@ public class OakOSGiRepositoryFactory im
//and not in a different thread
config.put("felix.fileinstall.noInitialDelay", "true");
- //Directory used by Felix File Install to watch for configs
config.put("repository.home", FilenameUtils.concat(home, "repository"));
copyConfigToSystemProps(config);
}
+ @SuppressWarnings("unchecked")
private static void copyConfigToSystemProps(Map config) {
//TODO This is a temporary workaround as the current release version
//of PojoSR reads value from System properties. Trunk version reads from
@@ -225,6 +236,7 @@ public class OakOSGiRepositoryFactory im
private void startBundles(PojoServiceRegistry registry) {
try {
List<BundleDescriptor> descriptors = new ClasspathScanner().scanForBundles();
+ descriptors = Lists.newArrayList(descriptors);
descriptors = processDescriptors(descriptors);
registry.startBundles(descriptors);
} catch (Exception e) {
Added: jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/ConfigTest.groovy
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/ConfigTest.groovy?rev=1597707&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/ConfigTest.groovy (added)
+++ jackrabbit/oak/trunk/oak-pojosr/src/test/groovy/org/apache/jackrabbit/oak/run/osgi/ConfigTest.groovy Tue May 27 06:14:29 2014
@@ -0,0 +1,143 @@
+/*
+ * 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.jackrabbit.oak.run.osgi
+
+import de.kalpatec.pojosr.framework.launch.BundleDescriptor
+import de.kalpatec.pojosr.framework.launch.PojoServiceRegistry
+import groovy.json.JsonOutput
+import org.apache.commons.io.FileUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.osgi.framework.Constants
+import org.osgi.service.cm.Configuration
+import org.osgi.service.cm.ConfigurationAdmin
+
+import static org.apache.jackrabbit.oak.run.osgi.OakOSGiRepositoryFactory.*
+
+class ConfigTest {
+ TestRepositoryFactory factory = new TestRepositoryFactory()
+ Map config
+ File workDir
+ PojoServiceRegistry registry
+ ConfigurationAdmin cm
+
+ @Before
+ void setUp(){
+ workDir = new File("target", "ConfigTest");
+ config = [
+ (REPOSITORY_HOME) : workDir.absolutePath,
+ 'magic.spell' : 'Alohomora'
+ ]
+ }
+
+ @After
+ void tearDown(){
+ if(workDir.exists()) {
+ FileUtils.cleanDirectory(workDir);
+ }
+ }
+
+ @Test
+ void testRuntimeConfig(){
+ config[REPOSITORY_CONFIG] = createConfigMap()
+
+ initRegistry(config)
+
+ assertConfig()
+ }
+
+ @Test
+ void testFileConfig(){
+ def jf1 = new File(workDir, "config1.json")
+ def jf2 = new File(workDir, "config2.json")
+ jf1 << JsonOutput.toJson(createConfigMap())
+ jf2 << JsonOutput.toJson([bar : [a:'a3', b:4]])
+ config[REPOSITORY_CONFIG_FILE] = "${jf1.absolutePath},${jf2.absolutePath}" as String
+
+ initRegistry(config)
+
+ assertConfig()
+
+ Configuration c1 = cm.getConfiguration('bar', null)
+ assert c1.properties
+ assert c1.properties.get('a') == 'a3'
+ assert c1.properties.get('b') == 4
+ }
+
+ @Test
+ void testConfigSync(){
+ config[REPOSITORY_CONFIG] = [
+ foo : [a:'a', b:1],
+ bar : [a:'a1', b:2]
+ ]
+ initRegistry(config)
+ Configuration c = cm.getConfiguration('baz')
+ c.update(new Hashtable([a :'a2']))
+
+ assert cm.getConfiguration('baz').properties.get('a') == 'a2'
+ assert cm.getConfiguration('foo').properties.get('a') == 'a'
+ assert cm.getConfiguration('bar').properties.get('a') == 'a1'
+
+ //Now re init and remove the pid bar
+ config[REPOSITORY_CONFIG] = [
+ foo : [a:'a-new', b:1],
+ ]
+ initRegistry(config)
+
+ assert cm.getConfiguration('baz').properties.get('a') == 'a2'
+ assert cm.getConfiguration('foo').properties.get('a') == 'a-new'
+ assert cm.getConfiguration('bar').properties == null
+ }
+
+ private static Map createConfigMap() {
+ [
+ 'foo' : [a: 'a', b: 1, c:'${magic.spell}'],
+ 'foo.bar-default': [a: 'a1', b: 2],
+ 'foo.bar-simple' : [a: 'a2', b: 3],
+ ]
+ }
+
+ private void assertConfig() {
+ Configuration c1 = cm.getConfiguration('foo', null)
+ assert c1.properties
+ assert c1.properties.get('a') == 'a'
+ assert c1.properties.get('b') == 1
+ assert c1.properties.get('c') == 'Alohomora'
+
+ Configuration[] fcs = cm.listConfigurations('(service.factoryPid=foo.bar)')
+ assert fcs.size() == 2
+ }
+
+ private void initRegistry(Map config){
+ registry = factory.initializeServiceRegistry(config)
+ cm = registry.getService(registry.getServiceReference(ConfigurationAdmin.class.name)) as ConfigurationAdmin
+ }
+
+ private static class TestRepositoryFactory extends OakOSGiRepositoryFactory {
+ @Override
+ protected List<BundleDescriptor> processDescriptors(List<BundleDescriptor> descriptors) {
+ //skip the oak bundles to prevent repository initialization
+ return super.processDescriptors(descriptors).findAll {BundleDescriptor bd ->
+ !bd.headers[Constants.BUNDLE_SYMBOLICNAME]?.startsWith('org.apache.jackrabbit')
+ }
+ }
+ }
+}